Skip to main content

polymorph_webcrypto_wasmtime/
host.rs

1//! Host trait implementations for the `polymorph:webcrypto` imports.
2//!
3//! Following the split the generated bindings produce (and mirroring
4//! `wasmtime_wasi_http::p3`), the store-free traits are implemented for the
5//! [`WasiWebcryptoCtxView`] "data" type, while the traits whose methods need
6//! the async `Accessor` are implemented for the [`WasiWebcrypto`] `HasData`
7//! marker.
8//!
9//! The cryptography itself lives in `polymorph-webcrypto-core`, shared verbatim
10//! with the in-guest provider; this module contributes only what is
11//! host-specific — the resource table and the shapes every operation
12//! shares (mint into the table, drain-then-compute, drain-then-stream-out),
13//! over the stream plumbing in [`crate::streams`] and the admission in
14//! [`crate::limits`].
15
16use polymorph_webcrypto_core::{
17    served_sha2, AeadKeyMaterial, DecryptionKeyMaterial, DigestKind, EncryptionKeyMaterial,
18    KwKeyMaterial, MacKeyMaterial, Sha1Posture, SigPublic, SigningKeyMaterial, WrapFormat,
19    WrapInputMaterial, HMAC_NAME,
20};
21use wasmtime::component::{Accessor, Resource, StreamReader};
22use wasmtime::Result;
23
24use crate::bindings::webcrypto::aead::{self, HostAeadKey, HostAeadKeyWithStore};
25use crate::bindings::webcrypto::cipher::{
26    self as cipher_iface, HostCipherKey, HostCipherKeyWithStore,
27};
28use crate::bindings::webcrypto::derivation::{
29    self as derivation_iface, HostDeriveInput, HostDeriveInputWithStore,
30};
31use crate::bindings::webcrypto::digest::{HostDigest, HostDigestWithStore};
32use crate::bindings::webcrypto::hkdf::{self as hkdf_iface, HostIkm, HostIkmWithStore};
33use crate::bindings::webcrypto::key_agreement::{
34    self as key_agreement_iface, HostPublicKey, HostPublicKeyWithStore, HostSecretKey,
35    HostSecretKeyWithStore,
36};
37use crate::bindings::webcrypto::key_wrap::{
38    self as key_wrap_iface, HostKwKey, HostKwKeyOptionsWithStore, HostKwKeyWithStore,
39};
40use crate::bindings::webcrypto::mac::{self, HostMacKey, HostMacKeyWithStore};
41use crate::bindings::webcrypto::pbkdf2::{
42    self as pbkdf2_iface, HostPassword, HostPasswordWithStore,
43};
44use crate::bindings::webcrypto::public_encryption::{
45    self as public_encryption_iface, HostDecryptionKey, HostDecryptionKeyWithStore,
46    HostEncryptionKey, HostEncryptionKeyWithStore,
47};
48use crate::bindings::webcrypto::types::{self, Error};
49use crate::bindings::webcrypto::wrapping::{
50    self as wrapping_iface, HostUnwrapInput, HostUnwrapInputWithStore, HostWrapInput,
51    HostWrapInputWithStore,
52};
53use crate::bindings::webcrypto::{
54    aes_cbc as aes_cbc_iface, aes_ctr as aes_ctr_iface, aes_gcm as aes_gcm_iface,
55    aes_kw as aes_kw_iface, digest as digest_iface, ecdh as ecdh_iface,
56    ecdsa_sign as ecdsa_sign_iface, ecdsa_verify as ecdsa_verify_iface,
57    ed25519_sign as ed25519_sign_iface, ed25519_verify as ed25519_verify_iface,
58    hkdf_sha1 as hkdf_sha1_iface, hkdf_sha2 as hkdf_sha2_iface, hmac_sha1 as hmac_sha1_iface,
59    hmac_sha2 as hmac_sha2_iface, pbkdf2_sha1 as pbkdf2_sha1_iface,
60    pbkdf2_sha2 as pbkdf2_sha2_iface, rsa as rsa_iface, rsa_oaep_decrypt as rsa_oaep_decrypt_iface,
61    rsa_oaep_encrypt as rsa_oaep_encrypt_iface, rsa_pss_sign as rsa_pss_sign_iface,
62    rsa_pss_verify as rsa_pss_verify_iface, rsassa_pkcs1_v15_sign as rsassa_sign_iface,
63    rsassa_pkcs1_v15_verify as rsassa_verify_iface, sha1_checked as sha1_checked_iface,
64    sha2 as sha2_iface, signature as signature_iface, x25519 as x25519_iface,
65};
66use crate::limits::{admit_input, Reservation};
67use crate::streams::{drain_stream, GuardedOutput};
68use crate::{
69    AeadKey, AgreementPublicKey, AgreementSecretKey, CipherKey, DecryptionKey, DeriveInput, Digest,
70    EncryptionKey, Ikm, KwKey, MacKey, Minted, Password, SigningKey, UnwrapInput, VerifyingKey,
71    WasiWebcrypto, WasiWebcryptoCtxView, WrapInput,
72};
73
74// --- bindings glue -------------------------------------------------------------
75
76polymorph_webcrypto_core::impl_conversions! {
77    error: Error,
78    extension: types::ExtensionError,
79    sha2: sha2_iface::Sha2Variant,
80    aes: aes_gcm_iface::AesVariant,
81    ecdsa: ecdsa_verify_iface::EcdsaVariant,
82    ecdh: ecdh_iface::EcdhVariant,
83    rsa: rsa_iface::RsaVariant,
84}
85
86/// Render an entropy failure as the trap-shaped host error for key or nonce
87/// generation: the host treats a failing random source as an operational
88/// host fault, never a guest-visible WIT error.
89fn rng_trap(what: &str) -> impl Fn(polymorph_webcrypto_core::RngError) -> wasmtime::Error + '_ {
90    move |err| wasmtime::Error::msg(format!("{what} failed: {err}"))
91}
92
93// --- shared operation shapes ---------------------------------------------------
94
95/// The message for a mint the retention budget cannot admit.
96fn retention_message(limit: u64) -> String {
97    format!(
98        "minted resources exceed the retention limit ({limit} bytes); see \
99         WasiWebcryptoCtx::set_retention_limit"
100    )
101}
102
103/// Render an exhausted retention budget as the WIT's recoverable
104/// operational error.
105fn retention_exhausted(limit: u64) -> Error {
106    Error::Other(retention_message(limit))
107}
108
109/// Charge one resource's retention floor, trap-shaped: for the
110/// `*-options.new` constructors, whose WIT signatures carry no error
111/// channel.
112fn charge_floor_or_trap(ctx: &crate::WasiWebcryptoCtx) -> Result<Reservation> {
113    ctx.charge_retention(0)
114        .ok_or_else(|| wasmtime::Error::msg(retention_message(ctx.retention_limit_bytes())))
115}
116
117/// Consume a `*-key-options` resource (the mint took ownership), yielding
118/// its accumulated state.
119async fn take_options<T: Send, O: Send + 'static>(
120    accessor: &Accessor<T, WasiWebcrypto>,
121    options: Resource<O>,
122) -> Result<O> {
123    accessor.with(|mut access| Ok(access.get().table.delete(options)?))
124}
125
126/// Charge a mint's retention (the per-resource floor plus the payload's
127/// variable-length material bytes, fail-fast — see `lib.rs`,
128/// "Minted-resource retention"), then push its outcome into the store's
129/// table: a successful mint becomes a resource handle assembled by
130/// [`Minted::minted`], carrying its reservation; a WIT error — the core's
131/// verdict, or the exhausted budget — flows to the caller.
132async fn mint<T: Send, R: Minted + Send + 'static>(
133    accessor: &Accessor<T, WasiWebcrypto>,
134    minted: std::result::Result<R::Payload, polymorph_webcrypto_core::Error>,
135) -> Result<std::result::Result<Resource<R>, Error>> {
136    let payload = match minted {
137        Ok(payload) => payload,
138        Err(err) => return Ok(Err(err.into())),
139    };
140    accessor.with(|mut access| {
141        let view = access.get();
142        match view.ctx.charge_retention(R::payload_bytes(&payload)) {
143            Some(retention) => Ok(Ok(view.table.push(R::minted(payload, retention))?)),
144            None => Ok(Err(retention_exhausted(view.ctx.retention_limit_bytes()))),
145        }
146    })
147}
148
149/// Charge the retention floor for each half of a generated key pair, then
150/// push both into the store's table (both halves are floor-only resources).
151/// An exhausted budget mints neither half.
152async fn mint_key_pair<T: Send, A, B>(
153    accessor: &Accessor<T, WasiWebcrypto>,
154    first: A::Payload,
155    second: B::Payload,
156) -> Result<std::result::Result<(Resource<A>, Resource<B>), Error>>
157where
158    A: Minted + Send + 'static,
159    B: Minted + Send + 'static,
160{
161    accessor.with(|mut access| {
162        let view = access.get();
163        let (Some(r1), Some(r2)) = (view.ctx.charge_retention(0), view.ctx.charge_retention(0))
164        else {
165            return Ok(Err(retention_exhausted(view.ctx.retention_limit_bytes())));
166        };
167        let a = view.table.push(A::minted(first, r1))?;
168        let b = view.table.push(B::minted(second, r2))?;
169        Ok(Ok((a, b)))
170    })
171}
172
173/// Run `op` on the table-held resource behind `self_`.
174async fn with_resource<T: Send, R: 'static, O>(
175    accessor: &Accessor<T, WasiWebcrypto>,
176    self_: Resource<R>,
177    op: impl FnOnce(&R) -> O,
178) -> Result<O> {
179    accessor.with(|mut access| Ok(op(access.get().table.get(&self_)?)))
180}
181
182/// Mint a `wrap-input` from a key's gated serialization: the shape every
183/// `to-wrap-input-*` shares.
184async fn to_wrap_input<T: Send, R: 'static>(
185    accessor: &Accessor<T, WasiWebcrypto>,
186    self_: Resource<R>,
187    format: WrapFormat,
188    serialize: impl FnOnce(&R) -> std::result::Result<Vec<u8>, polymorph_webcrypto_core::Error>,
189) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
190    let material = with_resource(accessor, self_, |key| {
191        serialize(key).map(|bytes| WrapInputMaterial::new(format, bytes))
192    })
193    .await?;
194    mint(accessor, material).await
195}
196
197/// Delete the table-held resource behind `rep` (the `drop` every key
198/// resource shares).
199async fn drop_resource<T: Send, R: 'static>(
200    accessor: &Accessor<T, WasiWebcrypto>,
201    rep: Resource<R>,
202) -> Result<()> {
203    accessor.with(|mut access| {
204        access.get().table.delete(rep)?;
205        Ok(())
206    })
207}
208
209/// The shared shape of every `*-options` resource: `new` charges the
210/// retention floor and mints an all-deny policy holder into the table, each
211/// setter writes one boolean policy field, and `drop` deletes the table
212/// entry. One invocation per options resource, listing its WIT setters as
213/// `method => policy field` rows.
214macro_rules! host_options {
215    (
216        $iface:ident::{$host:ident, $host_with_store:ident} for $ty:ty {
217            $($method:ident => $field:ident),+ $(,)?
218        }
219    ) => {
220        impl $iface::$host for WasiWebcryptoCtxView<'_> {
221            fn new(&mut self) -> Result<Resource<$ty>> {
222                let retention = charge_floor_or_trap(self.ctx)?;
223                Ok(self
224                    .table
225                    .push(<$ty>::minted(Default::default(), retention))?)
226            }
227
228            $(
229                fn $method(&mut self, self_: Resource<$ty>, allowed: bool) -> Result<()> {
230                    self.table.get_mut(&self_)?.policy.$field = allowed;
231                    Ok(())
232                }
233            )+
234        }
235
236        impl<T: Send> $iface::$host_with_store<T> for WasiWebcrypto {
237            async fn drop(accessor: &Accessor<T, Self>, rep: Resource<$ty>) -> Result<()> {
238                drop_resource(accessor, rep).await
239            }
240        }
241    };
242}
243
244/// Admit one operation, drain its whole input under the admitted cap, then
245/// run `op` on the table-held resource over the buffered bytes — the shape
246/// of every buffer-then-compute operation. Per the WIT contract the input
247/// stream is fully drained even when the call resolves with an error, so
248/// the caller's writer always completes.
249async fn drain_then<T: Send, R: 'static, O>(
250    accessor: &Accessor<T, WasiWebcrypto>,
251    self_: Resource<R>,
252    data: StreamReader<u8>,
253    op: impl FnOnce(&R, &[u8]) -> std::result::Result<O, Error>,
254) -> Result<std::result::Result<O, Error>> {
255    let (_reservation, cap) = admit_input(accessor).await?;
256    let bytes = match drain_stream(accessor, data, cap).await? {
257        Ok(bytes) => bytes,
258        Err(err) => return Ok(Err(err)),
259    };
260    accessor.with(|mut access| Ok(op(access.get().table.get(&self_)?, &bytes)))
261}
262
263/// Like [`drain_then`], for the seal/open shape: `op`'s output bytes are
264/// handed back as a stream whose producer carries the admission
265/// reservation, so pool capacity frees only when the bytes have left.
266/// (`op`'s outer `Result` is a trap-shaped host error — a failing nonce
267/// source.) Buffering the whole message is inherent to this shape: for
268/// `open`, no unverified plaintext may be observable.
269async fn drain_then_stream<T: Send, R: 'static>(
270    accessor: &Accessor<T, WasiWebcrypto>,
271    self_: Resource<R>,
272    input: StreamReader<u8>,
273    op: impl FnOnce(&mut R, &[u8]) -> Result<std::result::Result<Vec<u8>, Error>>,
274) -> Result<std::result::Result<StreamReader<u8>, Error>> {
275    let (reservation, cap) = admit_input(accessor).await?;
276    let msg = match drain_stream(accessor, input, cap).await? {
277        Ok(msg) => msg,
278        Err(err) => return Ok(Err(err)),
279    };
280    let out = accessor.with(|mut access| op(access.get().table.get_mut(&self_)?, &msg))?;
281    let out = match out {
282        Ok(out) => out,
283        Err(err) => return Ok(Err(err)),
284    };
285    let reader =
286        accessor.with(|access| StreamReader::new(access, GuardedOutput::new(out, reservation)))?;
287    Ok(Ok(reader))
288}
289
290// --- types -------------------------------------------------------------------
291
292impl types::Host for WasiWebcryptoCtxView<'_> {}
293
294// --- mac ---------------------------------------------------------------------
295
296impl mac::Host for WasiWebcryptoCtxView<'_> {}
297
298impl HostMacKey for WasiWebcryptoCtxView<'_> {
299    fn algorithm_name(&mut self, self_: Resource<MacKey>) -> Result<String> {
300        self.table.get(&self_)?;
301        Ok(HMAC_NAME.to_string())
302    }
303
304    fn algorithm_hash(&mut self, self_: Resource<MacKey>) -> Result<Option<String>> {
305        Ok(Some(
306            self.table.get(&self_)?.material.hash_name().to_string(),
307        ))
308    }
309
310    fn algorithm_length(&mut self, self_: Resource<MacKey>) -> Result<u32> {
311        Ok(self.table.get(&self_)?.material.length_bits())
312    }
313
314    fn extractable(&mut self, self_: Resource<MacKey>) -> Result<bool> {
315        Ok(self.table.get(&self_)?.material.extractable())
316    }
317
318    fn can_sign(&mut self, self_: Resource<MacKey>) -> Result<bool> {
319        Ok(self.table.get(&self_)?.material.can_sign())
320    }
321
322    fn can_verify(&mut self, self_: Resource<MacKey>) -> Result<bool> {
323        Ok(self.table.get(&self_)?.material.can_verify())
324    }
325}
326
327host_options! {
328    mac::{HostMacKeyOptions, HostMacKeyOptionsWithStore} for crate::MacKeyOptions {
329        can_sign => sign,
330        can_verify => verify,
331        extractable => extractable,
332    }
333}
334
335impl<T: Send> HostMacKeyWithStore<T> for WasiWebcrypto {
336    async fn sign(
337        accessor: &Accessor<T, Self>,
338        self_: Resource<MacKey>,
339        data: StreamReader<u8>,
340    ) -> Result<std::result::Result<Vec<u8>, Error>> {
341        // Buffer the whole stream, then fold it into the HMAC state; the
342        // result is chunking-invariant either way.
343        drain_then(accessor, self_, data, |key, bytes| {
344            key.material.sign(bytes).map_err(Error::from)
345        })
346        .await
347    }
348
349    async fn verify(
350        accessor: &Accessor<T, Self>,
351        self_: Resource<MacKey>,
352        data: StreamReader<u8>,
353        tag: Vec<u8>,
354    ) -> Result<std::result::Result<(), Error>> {
355        drain_then(accessor, self_, data, |key, bytes| {
356            key.material.verify(bytes, &tag).map_err(Error::from)
357        })
358        .await
359    }
360
361    async fn export_key_raw(
362        accessor: &Accessor<T, Self>,
363        self_: Resource<MacKey>,
364    ) -> Result<std::result::Result<Vec<u8>, Error>> {
365        with_resource(accessor, self_, |key| {
366            key.material.export().map_err(Error::from)
367        })
368        .await
369    }
370
371    async fn export_key_jwk(
372        accessor: &Accessor<T, Self>,
373        self_: Resource<MacKey>,
374    ) -> Result<std::result::Result<String, Error>> {
375        with_resource(accessor, self_, |key| {
376            key.material.export_jwk().map_err(Error::from)
377        })
378        .await
379    }
380
381    async fn to_wrap_input_raw(
382        accessor: &Accessor<T, Self>,
383        self_: Resource<MacKey>,
384    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
385        to_wrap_input(accessor, self_, WrapFormat::Raw, |key| {
386            key.material.export()
387        })
388        .await
389    }
390
391    async fn to_wrap_input_jwk(
392        accessor: &Accessor<T, Self>,
393        self_: Resource<MacKey>,
394    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
395        to_wrap_input(accessor, self_, WrapFormat::Jwk, |key| {
396            key.material.export_jwk().map(String::into_bytes)
397        })
398        .await
399    }
400
401    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<MacKey>) -> Result<()> {
402        drop_resource(accessor, rep).await
403    }
404}
405
406// --- aead --------------------------------------------------------------------
407
408impl aead::Host for WasiWebcryptoCtxView<'_> {}
409
410impl HostAeadKey for WasiWebcryptoCtxView<'_> {
411    fn algorithm_name(&mut self, self_: Resource<AeadKey>) -> Result<String> {
412        Ok(self.table.get(&self_)?.material.name().to_string())
413    }
414
415    fn algorithm_length(&mut self, self_: Resource<AeadKey>) -> Result<u32> {
416        Ok(self.table.get(&self_)?.material.length_bits())
417    }
418
419    fn nonce_size(&mut self, self_: Resource<AeadKey>) -> Result<u32> {
420        Ok(self.table.get(&self_)?.material.nonce_len() as u32)
421    }
422
423    fn tag_size(&mut self, self_: Resource<AeadKey>) -> Result<u32> {
424        Ok(self.table.get(&self_)?.material.tag_len() as u32)
425    }
426
427    fn extractable(&mut self, self_: Resource<AeadKey>) -> Result<bool> {
428        Ok(self.table.get(&self_)?.material.extractable())
429    }
430
431    fn can_seal(&mut self, self_: Resource<AeadKey>) -> Result<bool> {
432        Ok(self.table.get(&self_)?.material.can_seal())
433    }
434
435    fn can_open(&mut self, self_: Resource<AeadKey>) -> Result<bool> {
436        Ok(self.table.get(&self_)?.material.can_open())
437    }
438
439    fn can_wrap(&mut self, self_: Resource<AeadKey>) -> Result<bool> {
440        Ok(self.table.get(&self_)?.material.can_wrap())
441    }
442
443    fn can_unwrap(&mut self, self_: Resource<AeadKey>) -> Result<bool> {
444        Ok(self.table.get(&self_)?.material.can_unwrap())
445    }
446}
447
448host_options! {
449    aead::{HostAeadKeyOptions, HostAeadKeyOptionsWithStore} for crate::AeadKeyOptions {
450        can_seal => seal,
451        can_open => open,
452        can_wrap => wrap,
453        can_unwrap => unwrap,
454        extractable => extractable,
455    }
456}
457
458impl<T: Send> HostAeadKeyWithStore<T> for WasiWebcrypto {
459    async fn seal(
460        accessor: &Accessor<T, Self>,
461        self_: Resource<AeadKey>,
462        nonce: Vec<u8>,
463        aad: Vec<u8>,
464        tag_size: Option<u8>,
465        plaintext: StreamReader<u8>,
466    ) -> Result<std::result::Result<StreamReader<u8>, Error>> {
467        drain_then_stream(accessor, self_, plaintext, |key, msg| {
468            Ok(key
469                .material
470                .seal(&nonce, &aad, tag_size, msg)
471                .map_err(Error::from))
472        })
473        .await
474    }
475
476    async fn open(
477        accessor: &Accessor<T, Self>,
478        self_: Resource<AeadKey>,
479        nonce: Vec<u8>,
480        aad: Vec<u8>,
481        tag_size: Option<u8>,
482        ciphertext: StreamReader<u8>,
483    ) -> Result<std::result::Result<StreamReader<u8>, Error>> {
484        drain_then_stream(accessor, self_, ciphertext, |key, msg| {
485            Ok(key
486                .material
487                .open(&nonce, &aad, tag_size, msg)
488                .map_err(Error::from))
489        })
490        .await
491    }
492
493    async fn export_key_raw(
494        accessor: &Accessor<T, Self>,
495        self_: Resource<AeadKey>,
496    ) -> Result<std::result::Result<Vec<u8>, Error>> {
497        with_resource(accessor, self_, |key| {
498            key.material.export().map_err(Error::from)
499        })
500        .await
501    }
502
503    async fn export_key_jwk(
504        accessor: &Accessor<T, Self>,
505        self_: Resource<AeadKey>,
506    ) -> Result<std::result::Result<String, Error>> {
507        with_resource(accessor, self_, |key| {
508            key.material.export_jwk().map_err(Error::from)
509        })
510        .await
511    }
512
513    async fn wrap(
514        accessor: &Accessor<T, Self>,
515        self_: Resource<AeadKey>,
516        nonce: Vec<u8>,
517        aad: Vec<u8>,
518        tag_size: Option<u8>,
519        input: Resource<WrapInput>,
520    ) -> Result<std::result::Result<Vec<u8>, Error>> {
521        let input = take_options(accessor, input).await?.material;
522        with_resource(accessor, self_, |key| {
523            key.material
524                .wrap(&nonce, &aad, tag_size, input)
525                .map_err(Error::from)
526        })
527        .await
528    }
529
530    async fn unwrap(
531        accessor: &Accessor<T, Self>,
532        self_: Resource<AeadKey>,
533        nonce: Vec<u8>,
534        aad: Vec<u8>,
535        tag_size: Option<u8>,
536        wrapped: Vec<u8>,
537    ) -> Result<std::result::Result<Resource<UnwrapInput>, Error>> {
538        let material = with_resource(accessor, self_, |key| {
539            key.material
540                .unwrap_wrapped(&nonce, &aad, tag_size, &wrapped)
541        })
542        .await?;
543        mint(accessor, material).await
544    }
545
546    async fn to_wrap_input_raw(
547        accessor: &Accessor<T, Self>,
548        self_: Resource<AeadKey>,
549    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
550        to_wrap_input(accessor, self_, WrapFormat::Raw, |key| {
551            key.material.export()
552        })
553        .await
554    }
555
556    async fn to_wrap_input_jwk(
557        accessor: &Accessor<T, Self>,
558        self_: Resource<AeadKey>,
559    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
560        to_wrap_input(accessor, self_, WrapFormat::Jwk, |key| {
561            key.material.export_jwk().map(String::into_bytes)
562        })
563        .await
564    }
565
566    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<AeadKey>) -> Result<()> {
567        drop_resource(accessor, rep).await
568    }
569}
570
571// --- cipher (the unauthenticated-mode kind) --------------------------------------
572
573impl cipher_iface::Host for WasiWebcryptoCtxView<'_> {}
574
575impl HostCipherKey for WasiWebcryptoCtxView<'_> {
576    fn algorithm_name(&mut self, self_: Resource<CipherKey>) -> Result<String> {
577        Ok(self.table.get(&self_)?.material.name().to_string())
578    }
579
580    fn algorithm_length(&mut self, self_: Resource<CipherKey>) -> Result<u32> {
581        Ok(self.table.get(&self_)?.material.length_bits())
582    }
583
584    fn iv_size(&mut self, self_: Resource<CipherKey>) -> Result<u32> {
585        let _ = self.table.get(&self_)?;
586        Ok(16)
587    }
588
589    fn extractable(&mut self, self_: Resource<CipherKey>) -> Result<bool> {
590        Ok(self.table.get(&self_)?.material.policy().extractable)
591    }
592
593    fn can_encrypt(&mut self, self_: Resource<CipherKey>) -> Result<bool> {
594        Ok(self.table.get(&self_)?.material.policy().encrypt)
595    }
596
597    fn can_decrypt(&mut self, self_: Resource<CipherKey>) -> Result<bool> {
598        Ok(self.table.get(&self_)?.material.policy().decrypt)
599    }
600
601    fn can_wrap(&mut self, self_: Resource<CipherKey>) -> Result<bool> {
602        Ok(self.table.get(&self_)?.material.policy().wrap)
603    }
604
605    fn can_unwrap(&mut self, self_: Resource<CipherKey>) -> Result<bool> {
606        Ok(self.table.get(&self_)?.material.policy().unwrap)
607    }
608}
609
610host_options! {
611    cipher_iface::{HostCipherKeyOptions, HostCipherKeyOptionsWithStore}
612    for crate::CipherKeyOptions {
613        can_encrypt => encrypt,
614        can_decrypt => decrypt,
615        can_wrap => wrap,
616        can_unwrap => unwrap,
617        extractable => extractable,
618    }
619}
620
621impl<T: Send> HostCipherKeyWithStore<T> for WasiWebcrypto {
622    async fn encrypt(
623        accessor: &Accessor<T, Self>,
624        self_: Resource<CipherKey>,
625        iv: Vec<u8>,
626        counter_length: Option<u8>,
627        plaintext: StreamReader<u8>,
628    ) -> Result<std::result::Result<StreamReader<u8>, Error>> {
629        drain_then_stream(accessor, self_, plaintext, |key, msg| {
630            Ok(key
631                .material
632                .encrypt(&iv, counter_length, msg)
633                .map_err(Error::from))
634        })
635        .await
636    }
637
638    async fn decrypt(
639        accessor: &Accessor<T, Self>,
640        self_: Resource<CipherKey>,
641        iv: Vec<u8>,
642        counter_length: Option<u8>,
643        ciphertext: StreamReader<u8>,
644    ) -> Result<std::result::Result<StreamReader<u8>, Error>> {
645        drain_then_stream(accessor, self_, ciphertext, |key, msg| {
646            Ok(key
647                .material
648                .decrypt(&iv, counter_length, msg)
649                .map_err(Error::from))
650        })
651        .await
652    }
653
654    async fn export_key_raw(
655        accessor: &Accessor<T, Self>,
656        self_: Resource<CipherKey>,
657    ) -> Result<std::result::Result<Vec<u8>, Error>> {
658        with_resource(accessor, self_, |key| {
659            key.material.export().map_err(Error::from)
660        })
661        .await
662    }
663
664    async fn export_key_jwk(
665        accessor: &Accessor<T, Self>,
666        self_: Resource<CipherKey>,
667    ) -> Result<std::result::Result<String, Error>> {
668        with_resource(accessor, self_, |key| {
669            key.material.export_jwk().map_err(Error::from)
670        })
671        .await
672    }
673
674    async fn wrap(
675        accessor: &Accessor<T, Self>,
676        self_: Resource<CipherKey>,
677        iv: Vec<u8>,
678        counter_length: Option<u8>,
679        input: Resource<WrapInput>,
680    ) -> Result<std::result::Result<Vec<u8>, Error>> {
681        let input = take_options(accessor, input).await?.material;
682        with_resource(accessor, self_, |key| {
683            key.material
684                .wrap(&iv, counter_length, input)
685                .map_err(Error::from)
686        })
687        .await
688    }
689
690    async fn unwrap(
691        accessor: &Accessor<T, Self>,
692        self_: Resource<CipherKey>,
693        iv: Vec<u8>,
694        counter_length: Option<u8>,
695        wrapped: Vec<u8>,
696    ) -> Result<std::result::Result<Resource<UnwrapInput>, Error>> {
697        let material = with_resource(accessor, self_, |key| {
698            key.material.unwrap_wrapped(&iv, counter_length, &wrapped)
699        })
700        .await?;
701        mint(accessor, material).await
702    }
703
704    async fn to_wrap_input_raw(
705        accessor: &Accessor<T, Self>,
706        self_: Resource<CipherKey>,
707    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
708        to_wrap_input(accessor, self_, WrapFormat::Raw, |key| {
709            key.material.export()
710        })
711        .await
712    }
713
714    async fn to_wrap_input_jwk(
715        accessor: &Accessor<T, Self>,
716        self_: Resource<CipherKey>,
717    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
718        to_wrap_input(accessor, self_, WrapFormat::Jwk, |key| {
719            key.material.export_jwk().map(String::into_bytes)
720        })
721        .await
722    }
723
724    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<CipherKey>) -> Result<()> {
725        drop_resource(accessor, rep).await
726    }
727}
728
729// --- aes-cbc / aes-ctr (key minting) ----------------------------------------------
730
731/// The shared minting body of the two unauthenticated-mode interfaces:
732/// they differ only in the `CipherMode` they bind.
733macro_rules! cipher_minting {
734    ($iface:path, $mode:expr) => {
735        const _: () = {
736            use $iface as iface;
737
738            impl iface::Host for WasiWebcryptoCtxView<'_> {}
739
740            impl<T: Send> iface::HostWithStore<T> for WasiWebcrypto {
741                async fn import_key_raw(
742                    accessor: &Accessor<T, Self>,
743                    variant: iface::AesVariant,
744                    raw: Vec<u8>,
745                    options: Resource<crate::CipherKeyOptions>,
746                ) -> Result<std::result::Result<Resource<CipherKey>, Error>> {
747                    let policy = take_options(accessor, options).await?.policy;
748                    let material = polymorph_webcrypto_core::CipherKeyMaterial::import(
749                        $mode,
750                        variant.into(),
751                        raw,
752                        policy,
753                    );
754                    mint(accessor, material).await
755                }
756
757                async fn import_key_jwk(
758                    accessor: &Accessor<T, Self>,
759                    variant: iface::AesVariant,
760                    jwk: String,
761                    options: Resource<crate::CipherKeyOptions>,
762                ) -> Result<std::result::Result<Resource<CipherKey>, Error>> {
763                    let policy = take_options(accessor, options).await?.policy;
764                    let material = polymorph_webcrypto_core::CipherKeyMaterial::import_jwk(
765                        $mode,
766                        variant.into(),
767                        &jwk,
768                        policy,
769                    );
770                    mint(accessor, material).await
771                }
772
773                async fn generate_key(
774                    accessor: &Accessor<T, Self>,
775                    variant: iface::AesVariant,
776                    options: Resource<crate::CipherKeyOptions>,
777                ) -> Result<std::result::Result<Resource<CipherKey>, Error>> {
778                    let policy = take_options(accessor, options).await?.policy;
779                    let material = polymorph_webcrypto_core::CipherKeyMaterial::generate(
780                        $mode,
781                        variant.into(),
782                        policy,
783                    )
784                    .map_err(rng_trap("random key generation"))?;
785                    mint(accessor, material).await
786                }
787
788                async fn derive_key(
789                    accessor: &Accessor<T, Self>,
790                    variant: iface::AesVariant,
791                    input: Resource<DeriveInput>,
792                    options: Resource<crate::CipherKeyOptions>,
793                ) -> Result<std::result::Result<Resource<CipherKey>, Error>> {
794                    let policy = take_options(accessor, options).await?.policy;
795                    let material = with_resource(accessor, input, |input| {
796                        polymorph_webcrypto_core::derive_cipher_key(
797                            &input.material,
798                            $mode,
799                            variant.into(),
800                            policy,
801                        )
802                    })
803                    .await?;
804                    mint(accessor, material).await
805                }
806
807                async fn unwrap_key_raw(
808                    accessor: &Accessor<T, Self>,
809                    variant: iface::AesVariant,
810                    input: Resource<UnwrapInput>,
811                    options: Resource<crate::CipherKeyOptions>,
812                ) -> Result<std::result::Result<Resource<CipherKey>, Error>> {
813                    let policy = take_options(accessor, options).await?.policy;
814                    let input = take_options(accessor, input).await?.material;
815                    let material = polymorph_webcrypto_core::unwrap_cipher_key(
816                        $mode,
817                        variant.into(),
818                        input,
819                        policy,
820                    );
821                    mint(accessor, material).await
822                }
823
824                async fn unwrap_key_jwk(
825                    accessor: &Accessor<T, Self>,
826                    variant: iface::AesVariant,
827                    input: Resource<UnwrapInput>,
828                    options: Resource<crate::CipherKeyOptions>,
829                ) -> Result<std::result::Result<Resource<CipherKey>, Error>> {
830                    let policy = take_options(accessor, options).await?.policy;
831                    let input = take_options(accessor, input).await?.material;
832                    let material = polymorph_webcrypto_core::unwrap_cipher_key_jwk(
833                        $mode,
834                        variant.into(),
835                        input,
836                        policy,
837                    );
838                    mint(accessor, material).await
839                }
840            }
841        };
842    };
843}
844
845cipher_minting!(aes_cbc_iface, polymorph_webcrypto_core::CipherMode::Cbc);
846cipher_minting!(aes_ctr_iface, polymorph_webcrypto_core::CipherMode::Ctr);
847
848// --- wrapping (the provider-held intermediates) -----------------------------------
849
850impl wrapping_iface::Host for WasiWebcryptoCtxView<'_> {}
851
852impl HostWrapInput for WasiWebcryptoCtxView<'_> {}
853
854impl HostUnwrapInput for WasiWebcryptoCtxView<'_> {}
855
856impl<T: Send> HostWrapInputWithStore<T> for WasiWebcrypto {
857    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<WrapInput>) -> Result<()> {
858        drop_resource(accessor, rep).await
859    }
860}
861
862impl<T: Send> HostUnwrapInputWithStore<T> for WasiWebcrypto {
863    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<UnwrapInput>) -> Result<()> {
864        drop_resource(accessor, rep).await
865    }
866}
867
868// --- key-wrap ----------------------------------------------------------------
869
870impl key_wrap_iface::Host for WasiWebcryptoCtxView<'_> {}
871
872impl HostKwKey for WasiWebcryptoCtxView<'_> {
873    fn algorithm_name(&mut self, self_: Resource<KwKey>) -> Result<String> {
874        Ok(self.table.get(&self_)?.material.name().to_string())
875    }
876
877    fn algorithm_length(&mut self, self_: Resource<KwKey>) -> Result<u32> {
878        Ok(self.table.get(&self_)?.material.length_bits())
879    }
880
881    fn extractable(&mut self, self_: Resource<KwKey>) -> Result<bool> {
882        Ok(self.table.get(&self_)?.material.extractable())
883    }
884
885    fn can_wrap(&mut self, self_: Resource<KwKey>) -> Result<bool> {
886        Ok(self.table.get(&self_)?.material.can_wrap())
887    }
888
889    fn can_unwrap(&mut self, self_: Resource<KwKey>) -> Result<bool> {
890        Ok(self.table.get(&self_)?.material.can_unwrap())
891    }
892}
893
894impl key_wrap_iface::HostKwKeyOptions for WasiWebcryptoCtxView<'_> {
895    fn new(&mut self) -> Result<Resource<crate::KwKeyOptions>> {
896        let retention = charge_floor_or_trap(self.ctx)?;
897        Ok(self
898            .table
899            .push(crate::KwKeyOptions::minted(Default::default(), retention))?)
900    }
901
902    fn can_wrap(&mut self, self_: Resource<crate::KwKeyOptions>, allowed: bool) -> Result<()> {
903        self.table.get_mut(&self_)?.policy.wrap = allowed;
904        Ok(())
905    }
906
907    fn can_unwrap(&mut self, self_: Resource<crate::KwKeyOptions>, allowed: bool) -> Result<()> {
908        self.table.get_mut(&self_)?.policy.unwrap = allowed;
909        Ok(())
910    }
911
912    fn extractable(&mut self, self_: Resource<crate::KwKeyOptions>, allowed: bool) -> Result<()> {
913        self.table.get_mut(&self_)?.policy.extractable = allowed;
914        Ok(())
915    }
916}
917
918impl<T: Send> HostKwKeyOptionsWithStore<T> for WasiWebcrypto {
919    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<crate::KwKeyOptions>) -> Result<()> {
920        drop_resource(accessor, rep).await
921    }
922}
923
924impl<T: Send> HostKwKeyWithStore<T> for WasiWebcrypto {
925    async fn wrap(
926        accessor: &Accessor<T, Self>,
927        self_: Resource<KwKey>,
928        input: Resource<WrapInput>,
929    ) -> Result<std::result::Result<Vec<u8>, Error>> {
930        let input = take_options(accessor, input).await?.material;
931        with_resource(accessor, self_, |key| {
932            key.material.wrap(input).map_err(Error::from)
933        })
934        .await
935    }
936
937    async fn unwrap(
938        accessor: &Accessor<T, Self>,
939        self_: Resource<KwKey>,
940        wrapped: Vec<u8>,
941    ) -> Result<std::result::Result<Resource<UnwrapInput>, Error>> {
942        let material = with_resource(accessor, self_, |key| key.material.unwrap(&wrapped)).await?;
943        mint(accessor, material).await
944    }
945
946    async fn to_wrap_input_raw(
947        accessor: &Accessor<T, Self>,
948        self_: Resource<KwKey>,
949    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
950        to_wrap_input(accessor, self_, WrapFormat::Raw, |key| {
951            key.material.export()
952        })
953        .await
954    }
955
956    async fn to_wrap_input_jwk(
957        accessor: &Accessor<T, Self>,
958        self_: Resource<KwKey>,
959    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
960        to_wrap_input(accessor, self_, WrapFormat::Jwk, |key| {
961            key.material.export_jwk().map(String::into_bytes)
962        })
963        .await
964    }
965
966    async fn export_key_raw(
967        accessor: &Accessor<T, Self>,
968        self_: Resource<KwKey>,
969    ) -> Result<std::result::Result<Vec<u8>, Error>> {
970        with_resource(accessor, self_, |key| {
971            key.material.export().map_err(Error::from)
972        })
973        .await
974    }
975
976    async fn export_key_jwk(
977        accessor: &Accessor<T, Self>,
978        self_: Resource<KwKey>,
979    ) -> Result<std::result::Result<String, Error>> {
980        with_resource(accessor, self_, |key| {
981            key.material.export_jwk().map_err(Error::from)
982        })
983        .await
984    }
985
986    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<KwKey>) -> Result<()> {
987        drop_resource(accessor, rep).await
988    }
989}
990
991// --- aes-kw (key minting) --------------------------------------------------------
992
993impl aes_kw_iface::Host for WasiWebcryptoCtxView<'_> {}
994
995impl<T: Send> aes_kw_iface::HostWithStore<T> for WasiWebcrypto {
996    async fn import_key_raw(
997        accessor: &Accessor<T, Self>,
998        variant: aes_kw_iface::AesVariant,
999        raw: Vec<u8>,
1000        options: Resource<crate::KwKeyOptions>,
1001    ) -> Result<std::result::Result<Resource<KwKey>, Error>> {
1002        let policy = take_options(accessor, options).await?.policy;
1003        let material = KwKeyMaterial::import(variant.into(), raw, policy);
1004        mint(accessor, material).await
1005    }
1006
1007    async fn import_key_jwk(
1008        accessor: &Accessor<T, Self>,
1009        variant: aes_kw_iface::AesVariant,
1010        jwk: String,
1011        options: Resource<crate::KwKeyOptions>,
1012    ) -> Result<std::result::Result<Resource<KwKey>, Error>> {
1013        let policy = take_options(accessor, options).await?.policy;
1014        let material = KwKeyMaterial::import_jwk(variant.into(), &jwk, policy);
1015        mint(accessor, material).await
1016    }
1017
1018    async fn generate_key(
1019        accessor: &Accessor<T, Self>,
1020        variant: aes_kw_iface::AesVariant,
1021        options: Resource<crate::KwKeyOptions>,
1022    ) -> Result<std::result::Result<Resource<KwKey>, Error>> {
1023        let policy = take_options(accessor, options).await?.policy;
1024        let material = KwKeyMaterial::generate(variant.into(), policy)
1025            .map_err(rng_trap("random key generation"))?;
1026        mint(accessor, material).await
1027    }
1028
1029    async fn derive_key(
1030        accessor: &Accessor<T, Self>,
1031        variant: aes_kw_iface::AesVariant,
1032        input: Resource<DeriveInput>,
1033        options: Resource<crate::KwKeyOptions>,
1034    ) -> Result<std::result::Result<Resource<KwKey>, Error>> {
1035        let policy = take_options(accessor, options).await?.policy;
1036        let material = with_resource(accessor, input, |input| {
1037            polymorph_webcrypto_core::derive_kw_key(variant.into(), &input.material, policy)
1038        })
1039        .await?;
1040        mint(accessor, material).await
1041    }
1042
1043    async fn unwrap_key_raw(
1044        accessor: &Accessor<T, Self>,
1045        variant: aes_kw_iface::AesVariant,
1046        input: Resource<UnwrapInput>,
1047        options: Resource<crate::KwKeyOptions>,
1048    ) -> Result<std::result::Result<Resource<KwKey>, Error>> {
1049        let policy = take_options(accessor, options).await?.policy;
1050        let input = take_options(accessor, input).await?.material;
1051        let material = polymorph_webcrypto_core::unwrap_kw_key(variant.into(), input, policy);
1052        mint(accessor, material).await
1053    }
1054
1055    async fn unwrap_key_jwk(
1056        accessor: &Accessor<T, Self>,
1057        variant: aes_kw_iface::AesVariant,
1058        input: Resource<UnwrapInput>,
1059        options: Resource<crate::KwKeyOptions>,
1060    ) -> Result<std::result::Result<Resource<KwKey>, Error>> {
1061        let policy = take_options(accessor, options).await?.policy;
1062        let input = take_options(accessor, input).await?.material;
1063        let material = polymorph_webcrypto_core::unwrap_kw_key_jwk(variant.into(), input, policy);
1064        mint(accessor, material).await
1065    }
1066}
1067
1068// --- derivation -------------------------------------------------------------
1069
1070impl derivation_iface::Host for WasiWebcryptoCtxView<'_> {}
1071
1072host_options! {
1073    derivation_iface::{HostDeriveOptions, HostDeriveOptionsWithStore} for crate::DeriveOptions {
1074        can_derive_bits => derive_bits,
1075        can_derive_key => derive_key,
1076    }
1077}
1078
1079impl HostDeriveInput for WasiWebcryptoCtxView<'_> {
1080    fn can_derive_bits(&mut self, self_: Resource<DeriveInput>) -> Result<bool> {
1081        Ok(self.table.get(&self_)?.material.policy().derive_bits)
1082    }
1083
1084    fn can_derive_key(&mut self, self_: Resource<DeriveInput>) -> Result<bool> {
1085        Ok(self.table.get(&self_)?.material.policy().derive_key)
1086    }
1087}
1088
1089impl<T: Send> HostDeriveInputWithStore<T> for WasiWebcrypto {
1090    async fn derive_bits(
1091        accessor: &Accessor<T, Self>,
1092        self_: Resource<DeriveInput>,
1093        length: Option<u32>,
1094    ) -> Result<std::result::Result<Vec<u8>, Error>> {
1095        with_resource(accessor, self_, |input| {
1096            input
1097                .material
1098                .derive_bits(length)
1099                .map(|okm| okm.to_vec())
1100                .map_err(Error::from)
1101        })
1102        .await
1103    }
1104
1105    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<DeriveInput>) -> Result<()> {
1106        drop_resource(accessor, rep).await
1107    }
1108}
1109
1110// --- hkdf ----------------------------------------------------------------------
1111
1112impl hkdf_iface::Host for WasiWebcryptoCtxView<'_> {}
1113
1114impl HostIkm for WasiWebcryptoCtxView<'_> {
1115    fn can_derive_bits(&mut self, self_: Resource<Ikm>) -> Result<bool> {
1116        Ok(self.table.get(&self_)?.material.policy().derive_bits)
1117    }
1118
1119    fn can_derive_key(&mut self, self_: Resource<Ikm>) -> Result<bool> {
1120        Ok(self.table.get(&self_)?.material.policy().derive_key)
1121    }
1122}
1123
1124impl<T: Send> HostIkmWithStore<T> for WasiWebcrypto {
1125    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<Ikm>) -> Result<()> {
1126        drop_resource(accessor, rep).await
1127    }
1128}
1129
1130impl<T: Send> hkdf_iface::HostWithStore<T> for WasiWebcrypto {
1131    async fn import_ikm(
1132        accessor: &Accessor<T, Self>,
1133        raw: Vec<u8>,
1134        options: Resource<crate::DeriveOptions>,
1135    ) -> Result<std::result::Result<Resource<Ikm>, Error>> {
1136        let policy = take_options(accessor, options).await?.policy;
1137        let material = polymorph_webcrypto_core::IkmMaterial::import(raw, policy);
1138        mint(accessor, material).await
1139    }
1140
1141    async fn unwrap_ikm(
1142        accessor: &Accessor<T, Self>,
1143        input: Resource<UnwrapInput>,
1144        options: Resource<crate::DeriveOptions>,
1145    ) -> Result<std::result::Result<Resource<Ikm>, Error>> {
1146        let policy = take_options(accessor, options).await?.policy;
1147        let input = take_options(accessor, input).await?.material;
1148        let material = polymorph_webcrypto_core::unwrap_ikm(input, policy);
1149        mint(accessor, material).await
1150    }
1151}
1152
1153impl hkdf_sha2_iface::Host for WasiWebcryptoCtxView<'_> {}
1154
1155impl<T: Send> hkdf_sha2_iface::HostWithStore<T> for WasiWebcrypto {
1156    async fn prepare(
1157        accessor: &Accessor<T, Self>,
1158        variant: hkdf_sha2_iface::Sha2Variant,
1159        input: Resource<Ikm>,
1160        salt: Vec<u8>,
1161        info: Vec<u8>,
1162    ) -> Result<std::result::Result<Resource<DeriveInput>, Error>> {
1163        let material = with_resource(accessor, input, |ikm| {
1164            polymorph_webcrypto_core::DeriveInputMaterial::prepare(
1165                variant.into(),
1166                &ikm.material,
1167                &salt,
1168                info,
1169            )
1170        })
1171        .await?;
1172        mint(accessor, material).await
1173    }
1174
1175    async fn prepare_from(
1176        accessor: &Accessor<T, Self>,
1177        variant: hkdf_sha2_iface::Sha2Variant,
1178        input: Resource<DeriveInput>,
1179        salt: Vec<u8>,
1180        info: Vec<u8>,
1181    ) -> Result<std::result::Result<Resource<DeriveInput>, Error>> {
1182        let material = with_resource(accessor, input, |upstream| {
1183            polymorph_webcrypto_core::DeriveInputMaterial::prepare_from(
1184                variant.into(),
1185                &upstream.material,
1186                &salt,
1187                info,
1188            )
1189        })
1190        .await?;
1191        mint(accessor, material).await
1192    }
1193}
1194
1195// --- the SHA-1 constructions (hmac-sha1 / hkdf-sha1 / pbkdf2-sha1) ---------------
1196
1197impl hmac_sha1_iface::Host for WasiWebcryptoCtxView<'_> {}
1198
1199impl<T: Send> hmac_sha1_iface::HostWithStore<T> for WasiWebcrypto {
1200    async fn import_key_raw(
1201        accessor: &Accessor<T, Self>,
1202        raw: Vec<u8>,
1203        options: Resource<crate::MacKeyOptions>,
1204    ) -> Result<std::result::Result<Resource<MacKey>, Error>> {
1205        let policy = take_options(accessor, options).await?.policy;
1206        let material = MacKeyMaterial::import_sha1(raw, policy);
1207        mint(accessor, material).await
1208    }
1209
1210    async fn import_key_jwk(
1211        accessor: &Accessor<T, Self>,
1212        jwk: String,
1213        options: Resource<crate::MacKeyOptions>,
1214    ) -> Result<std::result::Result<Resource<MacKey>, Error>> {
1215        let policy = take_options(accessor, options).await?.policy;
1216        let material = MacKeyMaterial::import_jwk_sha1(&jwk, policy);
1217        mint(accessor, material).await
1218    }
1219
1220    async fn generate_key(
1221        accessor: &Accessor<T, Self>,
1222        length: Option<u32>,
1223        options: Resource<crate::MacKeyOptions>,
1224    ) -> Result<std::result::Result<Resource<MacKey>, Error>> {
1225        let policy = take_options(accessor, options).await?.policy;
1226        let material = MacKeyMaterial::generate_sha1(length, policy)
1227            .map_err(rng_trap("random key generation"))?;
1228        mint(accessor, material).await
1229    }
1230
1231    async fn derive_key(
1232        accessor: &Accessor<T, Self>,
1233        input: Resource<DeriveInput>,
1234        length: Option<u32>,
1235        options: Resource<crate::MacKeyOptions>,
1236    ) -> Result<std::result::Result<Resource<MacKey>, Error>> {
1237        let policy = take_options(accessor, options).await?.policy;
1238        let material = with_resource(accessor, input, |input| {
1239            polymorph_webcrypto_core::derive_mac_key_sha1(&input.material, length, policy)
1240        })
1241        .await?;
1242        mint(accessor, material).await
1243    }
1244
1245    async fn unwrap_key_raw(
1246        accessor: &Accessor<T, Self>,
1247        input: Resource<UnwrapInput>,
1248        options: Resource<crate::MacKeyOptions>,
1249    ) -> Result<std::result::Result<Resource<MacKey>, Error>> {
1250        let policy = take_options(accessor, options).await?.policy;
1251        let input = take_options(accessor, input).await?.material;
1252        let material = polymorph_webcrypto_core::unwrap_mac_key_sha1(input, policy);
1253        mint(accessor, material).await
1254    }
1255
1256    async fn unwrap_key_jwk(
1257        accessor: &Accessor<T, Self>,
1258        input: Resource<UnwrapInput>,
1259        options: Resource<crate::MacKeyOptions>,
1260    ) -> Result<std::result::Result<Resource<MacKey>, Error>> {
1261        let policy = take_options(accessor, options).await?.policy;
1262        let input = take_options(accessor, input).await?.material;
1263        let material = polymorph_webcrypto_core::unwrap_mac_key_jwk_sha1(input, policy);
1264        mint(accessor, material).await
1265    }
1266}
1267
1268impl hkdf_sha1_iface::Host for WasiWebcryptoCtxView<'_> {}
1269
1270impl<T: Send> hkdf_sha1_iface::HostWithStore<T> for WasiWebcrypto {
1271    async fn prepare(
1272        accessor: &Accessor<T, Self>,
1273        input: Resource<Ikm>,
1274        salt: Vec<u8>,
1275        info: Vec<u8>,
1276    ) -> Result<std::result::Result<Resource<DeriveInput>, Error>> {
1277        let material = with_resource(accessor, input, |ikm| {
1278            polymorph_webcrypto_core::DeriveInputMaterial::prepare_sha1(&ikm.material, &salt, info)
1279        })
1280        .await?;
1281        mint(accessor, material).await
1282    }
1283
1284    async fn prepare_from(
1285        accessor: &Accessor<T, Self>,
1286        input: Resource<DeriveInput>,
1287        salt: Vec<u8>,
1288        info: Vec<u8>,
1289    ) -> Result<std::result::Result<Resource<DeriveInput>, Error>> {
1290        let material = with_resource(accessor, input, |upstream| {
1291            polymorph_webcrypto_core::DeriveInputMaterial::prepare_from_sha1(
1292                &upstream.material,
1293                &salt,
1294                info,
1295            )
1296        })
1297        .await?;
1298        mint(accessor, material).await
1299    }
1300}
1301
1302impl pbkdf2_sha1_iface::Host for WasiWebcryptoCtxView<'_> {}
1303
1304impl<T: Send> pbkdf2_sha1_iface::HostWithStore<T> for WasiWebcrypto {
1305    async fn prepare(
1306        accessor: &Accessor<T, Self>,
1307        input: Resource<Password>,
1308        salt: Vec<u8>,
1309        iterations: u32,
1310    ) -> Result<std::result::Result<Resource<DeriveInput>, Error>> {
1311        let material = with_resource(accessor, input, |password| {
1312            polymorph_webcrypto_core::DeriveInputMaterial::prepare_pbkdf2_sha1(
1313                &password.material,
1314                salt.clone(),
1315                iterations,
1316            )
1317        })
1318        .await?;
1319        mint(accessor, material).await
1320    }
1321}
1322
1323// --- pbkdf2 --------------------------------------------------------------------
1324
1325impl pbkdf2_iface::Host for WasiWebcryptoCtxView<'_> {}
1326
1327impl HostPassword for WasiWebcryptoCtxView<'_> {
1328    fn can_derive_bits(&mut self, self_: Resource<Password>) -> Result<bool> {
1329        Ok(self.table.get(&self_)?.material.policy().derive_bits)
1330    }
1331
1332    fn can_derive_key(&mut self, self_: Resource<Password>) -> Result<bool> {
1333        Ok(self.table.get(&self_)?.material.policy().derive_key)
1334    }
1335}
1336
1337impl<T: Send> HostPasswordWithStore<T> for WasiWebcrypto {
1338    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<Password>) -> Result<()> {
1339        drop_resource(accessor, rep).await
1340    }
1341}
1342
1343impl<T: Send> pbkdf2_iface::HostWithStore<T> for WasiWebcrypto {
1344    async fn import_password(
1345        accessor: &Accessor<T, Self>,
1346        raw: Vec<u8>,
1347        options: Resource<crate::DeriveOptions>,
1348    ) -> Result<std::result::Result<Resource<Password>, Error>> {
1349        let policy = take_options(accessor, options).await?.policy;
1350        let material = polymorph_webcrypto_core::PasswordMaterial::import(raw, policy);
1351        mint(accessor, material).await
1352    }
1353
1354    async fn unwrap_password(
1355        accessor: &Accessor<T, Self>,
1356        input: Resource<UnwrapInput>,
1357        options: Resource<crate::DeriveOptions>,
1358    ) -> Result<std::result::Result<Resource<Password>, Error>> {
1359        let policy = take_options(accessor, options).await?.policy;
1360        let input = take_options(accessor, input).await?.material;
1361        let material = polymorph_webcrypto_core::unwrap_password(input, policy);
1362        mint(accessor, material).await
1363    }
1364}
1365
1366impl pbkdf2_sha2_iface::Host for WasiWebcryptoCtxView<'_> {}
1367
1368impl<T: Send> pbkdf2_sha2_iface::HostWithStore<T> for WasiWebcrypto {
1369    async fn prepare(
1370        accessor: &Accessor<T, Self>,
1371        variant: pbkdf2_sha2_iface::Sha2Variant,
1372        input: Resource<Password>,
1373        salt: Vec<u8>,
1374        iterations: u32,
1375    ) -> Result<std::result::Result<Resource<DeriveInput>, Error>> {
1376        let material = with_resource(accessor, input, |password| {
1377            polymorph_webcrypto_core::DeriveInputMaterial::prepare_pbkdf2(
1378                variant.into(),
1379                &password.material,
1380                salt,
1381                iterations,
1382            )
1383        })
1384        .await?;
1385        mint(accessor, material).await
1386    }
1387}
1388
1389// --- key-agreement -------------------------------------------------------------
1390
1391impl key_agreement_iface::Host for WasiWebcryptoCtxView<'_> {}
1392
1393host_options! {
1394    key_agreement_iface::{HostAgreementKeyOptions, HostAgreementKeyOptionsWithStore}
1395    for crate::AgreementKeyOptions {
1396        can_derive_bits => derive_bits,
1397        can_derive_key => derive_key,
1398        extractable => extractable,
1399    }
1400}
1401
1402impl HostPublicKey for WasiWebcryptoCtxView<'_> {
1403    fn algorithm_name(&mut self, self_: Resource<AgreementPublicKey>) -> Result<String> {
1404        Ok(self.table.get(&self_)?.material.name().to_string())
1405    }
1406}
1407
1408impl<T: Send> HostPublicKeyWithStore<T> for WasiWebcrypto {
1409    async fn export_key_raw(
1410        accessor: &Accessor<T, Self>,
1411        self_: Resource<AgreementPublicKey>,
1412    ) -> Result<std::result::Result<Vec<u8>, Error>> {
1413        with_resource(accessor, self_, |key| Ok(key.material.export())).await
1414    }
1415
1416    async fn export_key_jwk(
1417        accessor: &Accessor<T, Self>,
1418        self_: Resource<AgreementPublicKey>,
1419    ) -> Result<std::result::Result<String, Error>> {
1420        with_resource(accessor, self_, |key| Ok(key.material.export_jwk())).await
1421    }
1422
1423    async fn export_key_spki(
1424        accessor: &Accessor<T, Self>,
1425        self_: Resource<AgreementPublicKey>,
1426    ) -> Result<std::result::Result<Vec<u8>, Error>> {
1427        with_resource(accessor, self_, |key| Ok(key.material.export_spki())).await
1428    }
1429
1430    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<AgreementPublicKey>) -> Result<()> {
1431        drop_resource(accessor, rep).await
1432    }
1433}
1434
1435impl HostSecretKey for WasiWebcryptoCtxView<'_> {
1436    fn algorithm_name(&mut self, self_: Resource<AgreementSecretKey>) -> Result<String> {
1437        Ok(self.table.get(&self_)?.material.name().to_string())
1438    }
1439
1440    fn can_derive_bits(&mut self, self_: Resource<AgreementSecretKey>) -> Result<bool> {
1441        Ok(self.table.get(&self_)?.material.policy().derive_bits)
1442    }
1443
1444    fn can_derive_key(&mut self, self_: Resource<AgreementSecretKey>) -> Result<bool> {
1445        Ok(self.table.get(&self_)?.material.policy().derive_key)
1446    }
1447
1448    fn extractable(&mut self, self_: Resource<AgreementSecretKey>) -> Result<bool> {
1449        Ok(self.table.get(&self_)?.material.policy().extractable)
1450    }
1451}
1452
1453impl<T: Send> HostSecretKeyWithStore<T> for WasiWebcrypto {
1454    async fn agree(
1455        accessor: &Accessor<T, Self>,
1456        self_: Resource<AgreementSecretKey>,
1457        peer: Resource<AgreementPublicKey>,
1458    ) -> Result<std::result::Result<Resource<DeriveInput>, Error>> {
1459        let material = accessor.with(|mut access| -> Result<_> {
1460            let view = access.get();
1461            let secret = view.table.get(&self_)?;
1462            let peer = view.table.get(&peer)?;
1463            Ok(secret.material.agree(&peer.material))
1464        })?;
1465        mint(accessor, material).await
1466    }
1467
1468    async fn export_key_jwk(
1469        accessor: &Accessor<T, Self>,
1470        self_: Resource<AgreementSecretKey>,
1471    ) -> Result<std::result::Result<String, Error>> {
1472        with_resource(accessor, self_, |key| {
1473            key.material.export_jwk().map_err(Error::from)
1474        })
1475        .await
1476    }
1477
1478    async fn export_key_pkcs8(
1479        accessor: &Accessor<T, Self>,
1480        self_: Resource<AgreementSecretKey>,
1481    ) -> Result<std::result::Result<Vec<u8>, Error>> {
1482        with_resource(accessor, self_, |key| {
1483            key.material.export_pkcs8().map_err(Error::from)
1484        })
1485        .await
1486    }
1487
1488    async fn to_wrap_input_jwk(
1489        accessor: &Accessor<T, Self>,
1490        self_: Resource<AgreementSecretKey>,
1491    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
1492        to_wrap_input(accessor, self_, WrapFormat::Jwk, |key| {
1493            key.material.export_jwk().map(String::into_bytes)
1494        })
1495        .await
1496    }
1497
1498    async fn to_wrap_input_pkcs8(
1499        accessor: &Accessor<T, Self>,
1500        self_: Resource<AgreementSecretKey>,
1501    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
1502        to_wrap_input(accessor, self_, WrapFormat::Pkcs8, |key| {
1503            key.material.export_pkcs8()
1504        })
1505        .await
1506    }
1507
1508    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<AgreementSecretKey>) -> Result<()> {
1509        drop_resource(accessor, rep).await
1510    }
1511}
1512
1513// --- x25519 (key minting) --------------------------------------------------------
1514
1515impl x25519_iface::Host for WasiWebcryptoCtxView<'_> {}
1516
1517impl<T: Send> x25519_iface::HostWithStore<T> for WasiWebcrypto {
1518    async fn import_public_key_raw(
1519        accessor: &Accessor<T, Self>,
1520        raw: Vec<u8>,
1521    ) -> Result<std::result::Result<Resource<AgreementPublicKey>, Error>> {
1522        let material = polymorph_webcrypto_core::AgreementPublicMaterial::import_x25519(&raw);
1523        mint(accessor, material).await
1524    }
1525
1526    async fn import_public_key_spki(
1527        accessor: &Accessor<T, Self>,
1528        spki: Vec<u8>,
1529    ) -> Result<std::result::Result<Resource<AgreementPublicKey>, Error>> {
1530        let material = polymorph_webcrypto_core::AgreementPublicMaterial::import_x25519_spki(&spki);
1531        mint(accessor, material).await
1532    }
1533
1534    async fn import_public_key_jwk(
1535        accessor: &Accessor<T, Self>,
1536        jwk: String,
1537    ) -> Result<std::result::Result<Resource<AgreementPublicKey>, Error>> {
1538        let material = polymorph_webcrypto_core::AgreementPublicMaterial::import_x25519_jwk(&jwk);
1539        mint(accessor, material).await
1540    }
1541
1542    async fn import_secret_key_pkcs8(
1543        accessor: &Accessor<T, Self>,
1544        pkcs8: Vec<u8>,
1545        options: Resource<crate::AgreementKeyOptions>,
1546    ) -> Result<std::result::Result<Resource<AgreementSecretKey>, Error>> {
1547        let policy = take_options(accessor, options).await?.policy;
1548        let material =
1549            polymorph_webcrypto_core::AgreementSecretMaterial::import_x25519_pkcs8(&pkcs8, policy);
1550        mint(accessor, material).await
1551    }
1552
1553    async fn import_secret_key_jwk(
1554        accessor: &Accessor<T, Self>,
1555        jwk: String,
1556        options: Resource<crate::AgreementKeyOptions>,
1557    ) -> Result<std::result::Result<Resource<AgreementSecretKey>, Error>> {
1558        let policy = take_options(accessor, options).await?.policy;
1559        let material =
1560            polymorph_webcrypto_core::AgreementSecretMaterial::import_x25519_jwk(&jwk, policy);
1561        mint(accessor, material).await
1562    }
1563
1564    async fn generate_key(
1565        accessor: &Accessor<T, Self>,
1566        options: Resource<crate::AgreementKeyOptions>,
1567    ) -> Result<
1568        std::result::Result<(Resource<AgreementSecretKey>, Resource<AgreementPublicKey>), Error>,
1569    > {
1570        let policy = take_options(accessor, options).await?.policy;
1571        let material = polymorph_webcrypto_core::AgreementSecretMaterial::generate_x25519(policy)
1572            .map_err(rng_trap("random key generation"))?;
1573        match material {
1574            Ok((secret, public)) => mint_key_pair(accessor, secret, public).await,
1575            Err(err) => Ok(Err(err.into())),
1576        }
1577    }
1578
1579    async fn unwrap_secret_key_jwk(
1580        accessor: &Accessor<T, Self>,
1581        input: Resource<UnwrapInput>,
1582        options: Resource<crate::AgreementKeyOptions>,
1583    ) -> Result<std::result::Result<Resource<AgreementSecretKey>, Error>> {
1584        let policy = take_options(accessor, options).await?.policy;
1585        let input = take_options(accessor, input).await?.material;
1586        let material = polymorph_webcrypto_core::unwrap_x25519_secret_key_jwk(input, policy);
1587        mint(accessor, material).await
1588    }
1589
1590    async fn unwrap_secret_key_pkcs8(
1591        accessor: &Accessor<T, Self>,
1592        input: Resource<UnwrapInput>,
1593        options: Resource<crate::AgreementKeyOptions>,
1594    ) -> Result<std::result::Result<Resource<AgreementSecretKey>, Error>> {
1595        let policy = take_options(accessor, options).await?.policy;
1596        let input = take_options(accessor, input).await?.material;
1597        let material = polymorph_webcrypto_core::unwrap_x25519_secret_key_pkcs8(input, policy);
1598        mint(accessor, material).await
1599    }
1600}
1601
1602// --- ecdh (key minting) ----------------------------------------------------------
1603
1604impl ecdh_iface::Host for WasiWebcryptoCtxView<'_> {}
1605
1606impl<T: Send> ecdh_iface::HostWithStore<T> for WasiWebcrypto {
1607    async fn import_public_key_raw(
1608        accessor: &Accessor<T, Self>,
1609        variant: ecdh_iface::EcdhVariant,
1610        raw: Vec<u8>,
1611    ) -> Result<std::result::Result<Resource<AgreementPublicKey>, Error>> {
1612        let material =
1613            polymorph_webcrypto_core::AgreementPublicMaterial::import_ecdh(variant.into(), &raw);
1614        mint(accessor, material).await
1615    }
1616
1617    async fn import_public_key_spki(
1618        accessor: &Accessor<T, Self>,
1619        variant: ecdh_iface::EcdhVariant,
1620        spki: Vec<u8>,
1621    ) -> Result<std::result::Result<Resource<AgreementPublicKey>, Error>> {
1622        let material = polymorph_webcrypto_core::AgreementPublicMaterial::import_ecdh_spki(
1623            variant.into(),
1624            &spki,
1625        );
1626        mint(accessor, material).await
1627    }
1628
1629    async fn import_public_key_jwk(
1630        accessor: &Accessor<T, Self>,
1631        variant: ecdh_iface::EcdhVariant,
1632        jwk: String,
1633    ) -> Result<std::result::Result<Resource<AgreementPublicKey>, Error>> {
1634        let material = polymorph_webcrypto_core::AgreementPublicMaterial::import_ecdh_jwk(
1635            variant.into(),
1636            &jwk,
1637        );
1638        mint(accessor, material).await
1639    }
1640
1641    async fn import_secret_key_jwk(
1642        accessor: &Accessor<T, Self>,
1643        variant: ecdh_iface::EcdhVariant,
1644        jwk: String,
1645        options: Resource<crate::AgreementKeyOptions>,
1646    ) -> Result<std::result::Result<Resource<AgreementSecretKey>, Error>> {
1647        let policy = take_options(accessor, options).await?.policy;
1648        let material = polymorph_webcrypto_core::AgreementSecretMaterial::import_ecdh_jwk(
1649            variant.into(),
1650            &jwk,
1651            policy,
1652        );
1653        mint(accessor, material).await
1654    }
1655
1656    async fn import_secret_key_pkcs8(
1657        accessor: &Accessor<T, Self>,
1658        variant: ecdh_iface::EcdhVariant,
1659        pkcs8: Vec<u8>,
1660        options: Resource<crate::AgreementKeyOptions>,
1661    ) -> Result<std::result::Result<Resource<AgreementSecretKey>, Error>> {
1662        let policy = take_options(accessor, options).await?.policy;
1663        let material = polymorph_webcrypto_core::AgreementSecretMaterial::import_ecdh_pkcs8(
1664            variant.into(),
1665            &pkcs8,
1666            policy,
1667        );
1668        mint(accessor, material).await
1669    }
1670
1671    async fn generate_key(
1672        accessor: &Accessor<T, Self>,
1673        variant: ecdh_iface::EcdhVariant,
1674        options: Resource<crate::AgreementKeyOptions>,
1675    ) -> Result<
1676        std::result::Result<(Resource<AgreementSecretKey>, Resource<AgreementPublicKey>), Error>,
1677    > {
1678        let policy = take_options(accessor, options).await?.policy;
1679        let material = polymorph_webcrypto_core::AgreementSecretMaterial::generate_ecdh(
1680            variant.into(),
1681            policy,
1682        )
1683        .map_err(rng_trap("random key generation"))?;
1684        match material {
1685            Ok((secret, public)) => mint_key_pair(accessor, secret, public).await,
1686            Err(err) => Ok(Err(err.into())),
1687        }
1688    }
1689
1690    async fn unwrap_secret_key_jwk(
1691        accessor: &Accessor<T, Self>,
1692        variant: ecdh_iface::EcdhVariant,
1693        input: Resource<UnwrapInput>,
1694        options: Resource<crate::AgreementKeyOptions>,
1695    ) -> Result<std::result::Result<Resource<AgreementSecretKey>, Error>> {
1696        let policy = take_options(accessor, options).await?.policy;
1697        let input = take_options(accessor, input).await?.material;
1698        let material =
1699            polymorph_webcrypto_core::unwrap_ecdh_secret_key_jwk(variant.into(), input, policy);
1700        mint(accessor, material).await
1701    }
1702
1703    async fn unwrap_secret_key_pkcs8(
1704        accessor: &Accessor<T, Self>,
1705        variant: ecdh_iface::EcdhVariant,
1706        input: Resource<UnwrapInput>,
1707        options: Resource<crate::AgreementKeyOptions>,
1708    ) -> Result<std::result::Result<Resource<AgreementSecretKey>, Error>> {
1709        let policy = take_options(accessor, options).await?.policy;
1710        let input = take_options(accessor, input).await?.material;
1711        let material =
1712            polymorph_webcrypto_core::unwrap_ecdh_secret_key_pkcs8(variant.into(), input, policy);
1713        mint(accessor, material).await
1714    }
1715}
1716
1717// --- digest --------------------------------------------------------------------
1718
1719impl digest_iface::Host for WasiWebcryptoCtxView<'_> {}
1720
1721impl HostDigest for WasiWebcryptoCtxView<'_> {
1722    fn algorithm_name(&mut self, self_: Resource<Digest>) -> Result<String> {
1723        Ok(self.table.get(&self_)?.variant.hash_name().to_string())
1724    }
1725}
1726
1727impl<T: Send> HostDigestWithStore<T> for WasiWebcrypto {
1728    async fn compute(
1729        accessor: &Accessor<T, Self>,
1730        self_: Resource<Digest>,
1731        data: StreamReader<u8>,
1732    ) -> Result<std::result::Result<Vec<u8>, Error>> {
1733        // Buffer the whole stream, then hash it; the result is
1734        // chunking-invariant either way. The only error a compute can
1735        // report is checked SHA-1's `collision-detected` in the rejecting
1736        // posture.
1737        drain_then(accessor, self_, data, |digest, bytes| {
1738            digest.variant.digest(bytes).map_err(Into::into)
1739        })
1740        .await
1741    }
1742
1743    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<Digest>) -> Result<()> {
1744        drop_resource(accessor, rep).await
1745    }
1746}
1747
1748// --- sha2 (digest minting) ---------------------------------------------------
1749
1750impl sha2_iface::Host for WasiWebcryptoCtxView<'_> {
1751    fn make_digest(
1752        &mut self,
1753        variant: sha2_iface::Sha2Variant,
1754    ) -> Result<std::result::Result<Resource<Digest>, Error>> {
1755        let variant = match served_sha2(variant.into()) {
1756            Ok(variant) => variant,
1757            Err(err) => return Ok(Err(err.into())),
1758        };
1759        let Some(retention) = self.ctx.charge_retention(0) else {
1760            return Ok(Err(retention_exhausted(self.ctx.retention_limit_bytes())));
1761        };
1762        Ok(Ok(self.table.push(Digest::minted(
1763            DigestKind::Sha2(variant),
1764            retention,
1765        ))?))
1766    }
1767}
1768
1769// --- sha1-checked (digest minting) ---------------------------------------------
1770
1771impl sha1_checked_iface::Host for WasiWebcryptoCtxView<'_> {
1772    fn make_rejecting_digest(&mut self) -> Result<std::result::Result<Resource<Digest>, Error>> {
1773        let Some(retention) = self.ctx.charge_retention(0) else {
1774            return Ok(Err(retention_exhausted(self.ctx.retention_limit_bytes())));
1775        };
1776        Ok(Ok(self.table.push(Digest::minted(
1777            DigestKind::Sha1Checked(Sha1Posture::Reject),
1778            retention,
1779        ))?))
1780    }
1781
1782    fn make_mitigating_digest(&mut self) -> Result<std::result::Result<Resource<Digest>, Error>> {
1783        let Some(retention) = self.ctx.charge_retention(0) else {
1784            return Ok(Err(retention_exhausted(self.ctx.retention_limit_bytes())));
1785        };
1786        Ok(Ok(self.table.push(Digest::minted(
1787            DigestKind::Sha1Checked(Sha1Posture::Mitigate),
1788            retention,
1789        ))?))
1790    }
1791}
1792
1793// --- hmac-sha2 (key minting) -----------------------------------------------------
1794
1795impl hmac_sha2_iface::Host for WasiWebcryptoCtxView<'_> {}
1796
1797impl<T: Send> hmac_sha2_iface::HostWithStore<T> for WasiWebcrypto {
1798    async fn import_key_raw(
1799        accessor: &Accessor<T, Self>,
1800        variant: hmac_sha2_iface::Sha2Variant,
1801        raw: Vec<u8>,
1802        options: Resource<crate::MacKeyOptions>,
1803    ) -> Result<std::result::Result<Resource<MacKey>, Error>> {
1804        let policy = take_options(accessor, options).await?.policy;
1805        let material = MacKeyMaterial::import(variant.into(), raw, policy);
1806        mint(accessor, material).await
1807    }
1808
1809    async fn import_key_jwk(
1810        accessor: &Accessor<T, Self>,
1811        variant: hmac_sha2_iface::Sha2Variant,
1812        jwk: String,
1813        options: Resource<crate::MacKeyOptions>,
1814    ) -> Result<std::result::Result<Resource<MacKey>, Error>> {
1815        let policy = take_options(accessor, options).await?.policy;
1816        let material = MacKeyMaterial::import_jwk(variant.into(), &jwk, policy);
1817        mint(accessor, material).await
1818    }
1819
1820    async fn generate_key(
1821        accessor: &Accessor<T, Self>,
1822        variant: hmac_sha2_iface::Sha2Variant,
1823        length: Option<u32>,
1824        options: Resource<crate::MacKeyOptions>,
1825    ) -> Result<std::result::Result<Resource<MacKey>, Error>> {
1826        let policy = take_options(accessor, options).await?.policy;
1827        let material = MacKeyMaterial::generate(variant.into(), length, policy)
1828            .map_err(rng_trap("random key generation"))?;
1829        mint(accessor, material).await
1830    }
1831
1832    async fn derive_key(
1833        accessor: &Accessor<T, Self>,
1834        variant: hmac_sha2_iface::Sha2Variant,
1835        input: Resource<DeriveInput>,
1836        length: Option<u32>,
1837        options: Resource<crate::MacKeyOptions>,
1838    ) -> Result<std::result::Result<Resource<MacKey>, Error>> {
1839        let policy = take_options(accessor, options).await?.policy;
1840        let material = with_resource(accessor, input, |input| {
1841            polymorph_webcrypto_core::derive_mac_key(
1842                &input.material,
1843                variant.into(),
1844                length,
1845                policy,
1846            )
1847        })
1848        .await?;
1849        mint(accessor, material).await
1850    }
1851
1852    async fn unwrap_key_raw(
1853        accessor: &Accessor<T, Self>,
1854        variant: hmac_sha2_iface::Sha2Variant,
1855        input: Resource<UnwrapInput>,
1856        options: Resource<crate::MacKeyOptions>,
1857    ) -> Result<std::result::Result<Resource<MacKey>, Error>> {
1858        let policy = take_options(accessor, options).await?.policy;
1859        let input = take_options(accessor, input).await?.material;
1860        let material = polymorph_webcrypto_core::unwrap_mac_key(variant.into(), input, policy);
1861        mint(accessor, material).await
1862    }
1863
1864    async fn unwrap_key_jwk(
1865        accessor: &Accessor<T, Self>,
1866        variant: hmac_sha2_iface::Sha2Variant,
1867        input: Resource<UnwrapInput>,
1868        options: Resource<crate::MacKeyOptions>,
1869    ) -> Result<std::result::Result<Resource<MacKey>, Error>> {
1870        let policy = take_options(accessor, options).await?.policy;
1871        let input = take_options(accessor, input).await?.material;
1872        let material = polymorph_webcrypto_core::unwrap_mac_key_jwk(variant.into(), input, policy);
1873        mint(accessor, material).await
1874    }
1875}
1876
1877// --- aes-gcm (key minting) -------------------------------------------------------
1878
1879impl aes_gcm_iface::Host for WasiWebcryptoCtxView<'_> {}
1880
1881impl<T: Send> aes_gcm_iface::HostWithStore<T> for WasiWebcrypto {
1882    async fn import_key_raw(
1883        accessor: &Accessor<T, Self>,
1884        variant: aes_gcm_iface::AesVariant,
1885        raw: Vec<u8>,
1886        options: Resource<crate::AeadKeyOptions>,
1887    ) -> Result<std::result::Result<Resource<AeadKey>, Error>> {
1888        let policy = take_options(accessor, options).await?.policy;
1889        let material = AeadKeyMaterial::import_aes_gcm(variant.into(), raw, policy);
1890        mint(accessor, material).await
1891    }
1892
1893    async fn import_key_jwk(
1894        accessor: &Accessor<T, Self>,
1895        variant: aes_gcm_iface::AesVariant,
1896        jwk: String,
1897        options: Resource<crate::AeadKeyOptions>,
1898    ) -> Result<std::result::Result<Resource<AeadKey>, Error>> {
1899        let policy = take_options(accessor, options).await?.policy;
1900        let material = AeadKeyMaterial::import_aes_gcm_jwk(variant.into(), &jwk, policy);
1901        mint(accessor, material).await
1902    }
1903
1904    async fn generate_key(
1905        accessor: &Accessor<T, Self>,
1906        variant: aes_gcm_iface::AesVariant,
1907        options: Resource<crate::AeadKeyOptions>,
1908    ) -> Result<std::result::Result<Resource<AeadKey>, Error>> {
1909        let policy = take_options(accessor, options).await?.policy;
1910        let material = AeadKeyMaterial::generate_aes_gcm(variant.into(), policy)
1911            .map_err(rng_trap("random key generation"))?;
1912        mint(accessor, material).await
1913    }
1914
1915    async fn derive_key(
1916        accessor: &Accessor<T, Self>,
1917        variant: aes_gcm_iface::AesVariant,
1918        input: Resource<DeriveInput>,
1919        options: Resource<crate::AeadKeyOptions>,
1920    ) -> Result<std::result::Result<Resource<AeadKey>, Error>> {
1921        let policy = take_options(accessor, options).await?.policy;
1922        let material = with_resource(accessor, input, |input| {
1923            polymorph_webcrypto_core::derive_aes_gcm_key(&input.material, variant.into(), policy)
1924        })
1925        .await?;
1926        mint(accessor, material).await
1927    }
1928
1929    async fn unwrap_key_raw(
1930        accessor: &Accessor<T, Self>,
1931        variant: aes_gcm_iface::AesVariant,
1932        input: Resource<UnwrapInput>,
1933        options: Resource<crate::AeadKeyOptions>,
1934    ) -> Result<std::result::Result<Resource<AeadKey>, Error>> {
1935        let policy = take_options(accessor, options).await?.policy;
1936        let input = take_options(accessor, input).await?.material;
1937        let material = polymorph_webcrypto_core::unwrap_aes_gcm_key(variant.into(), input, policy);
1938        mint(accessor, material).await
1939    }
1940
1941    async fn unwrap_key_jwk(
1942        accessor: &Accessor<T, Self>,
1943        variant: aes_gcm_iface::AesVariant,
1944        input: Resource<UnwrapInput>,
1945        options: Resource<crate::AeadKeyOptions>,
1946    ) -> Result<std::result::Result<Resource<AeadKey>, Error>> {
1947        let policy = take_options(accessor, options).await?.policy;
1948        let input = take_options(accessor, input).await?.material;
1949        let material =
1950            polymorph_webcrypto_core::unwrap_aes_gcm_key_jwk(variant.into(), input, policy);
1951        mint(accessor, material).await
1952    }
1953}
1954
1955// --- signature -----------------------------------------------------------------
1956
1957impl signature_iface::Host for WasiWebcryptoCtxView<'_> {}
1958
1959impl signature_iface::HostVerifyingKey for WasiWebcryptoCtxView<'_> {
1960    fn algorithm_name(&mut self, self_: Resource<VerifyingKey>) -> Result<String> {
1961        Ok(self.table.get(&self_)?.public.name().to_string())
1962    }
1963
1964    fn algorithm_curve(&mut self, self_: Resource<VerifyingKey>) -> Result<Option<String>> {
1965        Ok(self.table.get(&self_)?.public.curve().map(str::to_string))
1966    }
1967
1968    fn algorithm_hash(&mut self, self_: Resource<VerifyingKey>) -> Result<Option<String>> {
1969        Ok(self.table.get(&self_)?.public.hash().map(str::to_string))
1970    }
1971
1972    fn algorithm_length(&mut self, self_: Resource<VerifyingKey>) -> Result<Option<u32>> {
1973        Ok(self.table.get(&self_)?.public.length())
1974    }
1975
1976    fn algorithm_public_exponent(
1977        &mut self,
1978        self_: Resource<VerifyingKey>,
1979    ) -> Result<Option<Vec<u8>>> {
1980        Ok(self.table.get(&self_)?.public.public_exponent())
1981    }
1982}
1983
1984impl<T: Send> signature_iface::HostVerifyingKeyWithStore<T> for WasiWebcrypto {
1985    async fn verify(
1986        accessor: &Accessor<T, Self>,
1987        self_: Resource<VerifyingKey>,
1988        data: StreamReader<u8>,
1989        sig: Vec<u8>,
1990    ) -> Result<std::result::Result<(), Error>> {
1991        drain_then(accessor, self_, data, |key, bytes| {
1992            key.public.verify(bytes, &sig).map_err(Error::from)
1993        })
1994        .await
1995    }
1996
1997    async fn export_key_raw(
1998        accessor: &Accessor<T, Self>,
1999        self_: Resource<VerifyingKey>,
2000    ) -> Result<std::result::Result<Vec<u8>, Error>> {
2001        with_resource(accessor, self_, |key| {
2002            key.public.export().map_err(Error::from)
2003        })
2004        .await
2005    }
2006
2007    async fn export_key_spki(
2008        accessor: &Accessor<T, Self>,
2009        self_: Resource<VerifyingKey>,
2010    ) -> Result<std::result::Result<Vec<u8>, Error>> {
2011        with_resource(accessor, self_, |key| Ok(key.public.export_spki())).await
2012    }
2013
2014    async fn export_key_jwk(
2015        accessor: &Accessor<T, Self>,
2016        self_: Resource<VerifyingKey>,
2017    ) -> Result<std::result::Result<String, Error>> {
2018        with_resource(accessor, self_, |key| Ok(key.public.export_jwk())).await
2019    }
2020
2021    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<VerifyingKey>) -> Result<()> {
2022        drop_resource(accessor, rep).await
2023    }
2024}
2025
2026impl signature_iface::HostSigningKey for WasiWebcryptoCtxView<'_> {
2027    fn algorithm_name(&mut self, self_: Resource<SigningKey>) -> Result<String> {
2028        Ok(self.table.get(&self_)?.material.name().to_string())
2029    }
2030
2031    fn algorithm_curve(&mut self, self_: Resource<SigningKey>) -> Result<Option<String>> {
2032        Ok(self.table.get(&self_)?.material.curve().map(str::to_string))
2033    }
2034
2035    fn algorithm_hash(&mut self, self_: Resource<SigningKey>) -> Result<Option<String>> {
2036        Ok(self.table.get(&self_)?.material.hash().map(str::to_string))
2037    }
2038
2039    fn algorithm_length(&mut self, self_: Resource<SigningKey>) -> Result<Option<u32>> {
2040        Ok(self.table.get(&self_)?.material.length())
2041    }
2042
2043    fn algorithm_public_exponent(
2044        &mut self,
2045        self_: Resource<SigningKey>,
2046    ) -> Result<Option<Vec<u8>>> {
2047        Ok(self.table.get(&self_)?.material.public_exponent())
2048    }
2049
2050    fn extractable(&mut self, self_: Resource<SigningKey>) -> Result<bool> {
2051        Ok(self.table.get(&self_)?.material.extractable())
2052    }
2053
2054    fn can_sign(&mut self, self_: Resource<SigningKey>) -> Result<bool> {
2055        Ok(self.table.get(&self_)?.material.can_sign())
2056    }
2057}
2058
2059host_options! {
2060    signature_iface::{HostSigningKeyOptions, HostSigningKeyOptionsWithStore}
2061    for crate::SigningKeyOptions {
2062        can_sign => sign,
2063        extractable => extractable,
2064    }
2065}
2066
2067impl<T: Send> signature_iface::HostSigningKeyWithStore<T> for WasiWebcrypto {
2068    async fn sign(
2069        accessor: &Accessor<T, Self>,
2070        self_: Resource<SigningKey>,
2071        data: StreamReader<u8>,
2072    ) -> Result<std::result::Result<Vec<u8>, Error>> {
2073        drain_then(accessor, self_, data, |key, bytes| {
2074            key.material.sign(bytes).map_err(Error::from)
2075        })
2076        .await
2077    }
2078
2079    async fn export_key_jwk(
2080        accessor: &Accessor<T, Self>,
2081        self_: Resource<SigningKey>,
2082    ) -> Result<std::result::Result<String, Error>> {
2083        with_resource(accessor, self_, |key| {
2084            key.material.export_jwk().map_err(Error::from)
2085        })
2086        .await
2087    }
2088
2089    async fn export_key_pkcs8(
2090        accessor: &Accessor<T, Self>,
2091        self_: Resource<SigningKey>,
2092    ) -> Result<std::result::Result<Vec<u8>, Error>> {
2093        with_resource(accessor, self_, |key| {
2094            key.material.export_pkcs8().map_err(Error::from)
2095        })
2096        .await
2097    }
2098
2099    async fn to_wrap_input_jwk(
2100        accessor: &Accessor<T, Self>,
2101        self_: Resource<SigningKey>,
2102    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
2103        to_wrap_input(accessor, self_, WrapFormat::Jwk, |key| {
2104            key.material.export_jwk().map(String::into_bytes)
2105        })
2106        .await
2107    }
2108
2109    async fn to_wrap_input_pkcs8(
2110        accessor: &Accessor<T, Self>,
2111        self_: Resource<SigningKey>,
2112    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
2113        to_wrap_input(accessor, self_, WrapFormat::Pkcs8, |key| {
2114            key.material.export_pkcs8()
2115        })
2116        .await
2117    }
2118
2119    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<SigningKey>) -> Result<()> {
2120        drop_resource(accessor, rep).await
2121    }
2122}
2123
2124// --- ed25519 (key minting) -----------------------------------------------------
2125
2126impl ed25519_verify_iface::Host for WasiWebcryptoCtxView<'_> {}
2127impl ed25519_sign_iface::Host for WasiWebcryptoCtxView<'_> {}
2128
2129impl<T: Send> ed25519_verify_iface::HostWithStore<T> for WasiWebcrypto {
2130    async fn import_verifying_key_raw(
2131        accessor: &Accessor<T, Self>,
2132        raw: Vec<u8>,
2133    ) -> Result<std::result::Result<Resource<VerifyingKey>, Error>> {
2134        let public = SigPublic::import_ed25519(&raw);
2135        mint(accessor, public).await
2136    }
2137
2138    async fn import_verifying_key_spki(
2139        accessor: &Accessor<T, Self>,
2140        spki: Vec<u8>,
2141    ) -> Result<std::result::Result<Resource<VerifyingKey>, Error>> {
2142        let public = SigPublic::import_ed25519_spki(&spki);
2143        mint(accessor, public).await
2144    }
2145
2146    async fn import_verifying_key_jwk(
2147        accessor: &Accessor<T, Self>,
2148        jwk: String,
2149    ) -> Result<std::result::Result<Resource<VerifyingKey>, Error>> {
2150        let public = SigPublic::import_ed25519_jwk(&jwk);
2151        mint(accessor, public).await
2152    }
2153}
2154
2155impl<T: Send> ed25519_sign_iface::HostWithStore<T> for WasiWebcrypto {
2156    async fn generate_key(
2157        accessor: &Accessor<T, Self>,
2158        options: Resource<crate::SigningKeyOptions>,
2159    ) -> Result<std::result::Result<(Resource<SigningKey>, Resource<VerifyingKey>), Error>> {
2160        let policy = take_options(accessor, options).await?.policy;
2161        let material = match SigningKeyMaterial::generate_ed25519(policy)
2162            .map_err(rng_trap("random key generation"))?
2163        {
2164            Ok(material) => material,
2165            Err(err) => return Ok(Err(err.into())),
2166        };
2167        mint_signing_pair(accessor, material).await
2168    }
2169
2170    async fn import_signing_key_pkcs8(
2171        accessor: &Accessor<T, Self>,
2172        pkcs8: Vec<u8>,
2173        options: Resource<crate::SigningKeyOptions>,
2174    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2175        let policy = take_options(accessor, options).await?.policy;
2176        let material = SigningKeyMaterial::import_ed25519_pkcs8(&pkcs8, policy);
2177        mint(accessor, material).await
2178    }
2179
2180    async fn import_signing_key_jwk(
2181        accessor: &Accessor<T, Self>,
2182        jwk: String,
2183        options: Resource<crate::SigningKeyOptions>,
2184    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2185        let policy = take_options(accessor, options).await?.policy;
2186        let material = SigningKeyMaterial::import_ed25519_jwk(&jwk, policy);
2187        mint(accessor, material).await
2188    }
2189
2190    async fn unwrap_signing_key_pkcs8(
2191        accessor: &Accessor<T, Self>,
2192        input: Resource<UnwrapInput>,
2193        options: Resource<crate::SigningKeyOptions>,
2194    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2195        let policy = take_options(accessor, options).await?.policy;
2196        let input = take_options(accessor, input).await?.material;
2197        let material = polymorph_webcrypto_core::unwrap_ed25519_signing_key_pkcs8(input, policy);
2198        mint(accessor, material).await
2199    }
2200
2201    async fn unwrap_signing_key_jwk(
2202        accessor: &Accessor<T, Self>,
2203        input: Resource<UnwrapInput>,
2204        options: Resource<crate::SigningKeyOptions>,
2205    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2206        let policy = take_options(accessor, options).await?.policy;
2207        let input = take_options(accessor, input).await?.material;
2208        let material = polymorph_webcrypto_core::unwrap_ed25519_signing_key_jwk(input, policy);
2209        mint(accessor, material).await
2210    }
2211}
2212
2213/// Push a generated signing key and the public half returned with it.
2214async fn mint_signing_pair<T: Send>(
2215    accessor: &Accessor<T, WasiWebcrypto>,
2216    material: SigningKeyMaterial,
2217) -> Result<std::result::Result<(Resource<SigningKey>, Resource<VerifyingKey>), Error>> {
2218    let public = material.public();
2219    mint_key_pair(accessor, material, public).await
2220}
2221
2222// --- ecdsa (key minting) ---------------------------------------------------------
2223
2224impl ecdsa_verify_iface::Host for WasiWebcryptoCtxView<'_> {}
2225impl ecdsa_sign_iface::Host for WasiWebcryptoCtxView<'_> {}
2226
2227impl<T: Send> ecdsa_verify_iface::HostWithStore<T> for WasiWebcrypto {
2228    async fn import_verifying_key_raw(
2229        accessor: &Accessor<T, Self>,
2230        variant: ecdsa_verify_iface::EcdsaVariant,
2231        raw: Vec<u8>,
2232    ) -> Result<std::result::Result<Resource<VerifyingKey>, Error>> {
2233        let public = SigPublic::import_ecdsa(variant.into(), &raw);
2234        mint(accessor, public).await
2235    }
2236
2237    async fn import_verifying_key_spki(
2238        accessor: &Accessor<T, Self>,
2239        variant: ecdsa_verify_iface::EcdsaVariant,
2240        spki: Vec<u8>,
2241    ) -> Result<std::result::Result<Resource<VerifyingKey>, Error>> {
2242        let public = SigPublic::import_ecdsa_spki(variant.into(), &spki);
2243        mint(accessor, public).await
2244    }
2245
2246    async fn import_verifying_key_jwk(
2247        accessor: &Accessor<T, Self>,
2248        variant: ecdsa_verify_iface::EcdsaVariant,
2249        jwk: String,
2250    ) -> Result<std::result::Result<Resource<VerifyingKey>, Error>> {
2251        let public = SigPublic::import_ecdsa_jwk(variant.into(), &jwk);
2252        mint(accessor, public).await
2253    }
2254}
2255
2256impl<T: Send> ecdsa_sign_iface::HostWithStore<T> for WasiWebcrypto {
2257    async fn generate_key(
2258        accessor: &Accessor<T, Self>,
2259        variant: ecdsa_verify_iface::EcdsaVariant,
2260        options: Resource<crate::SigningKeyOptions>,
2261    ) -> Result<std::result::Result<(Resource<SigningKey>, Resource<VerifyingKey>), Error>> {
2262        let policy = take_options(accessor, options).await?.policy;
2263        let material = match SigningKeyMaterial::generate_ecdsa(variant.into(), policy)
2264            .map_err(rng_trap("random key generation"))?
2265        {
2266            Ok(material) => material,
2267            Err(err) => return Ok(Err(err.into())),
2268        };
2269        mint_signing_pair(accessor, material).await
2270    }
2271
2272    async fn import_signing_key_pkcs8(
2273        accessor: &Accessor<T, Self>,
2274        variant: ecdsa_verify_iface::EcdsaVariant,
2275        pkcs8: Vec<u8>,
2276        options: Resource<crate::SigningKeyOptions>,
2277    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2278        let policy = take_options(accessor, options).await?.policy;
2279        let material = SigningKeyMaterial::import_ecdsa_pkcs8(variant.into(), &pkcs8, policy);
2280        mint(accessor, material).await
2281    }
2282
2283    async fn import_signing_key_jwk(
2284        accessor: &Accessor<T, Self>,
2285        variant: ecdsa_verify_iface::EcdsaVariant,
2286        jwk: String,
2287        options: Resource<crate::SigningKeyOptions>,
2288    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2289        let policy = take_options(accessor, options).await?.policy;
2290        let material = SigningKeyMaterial::import_ecdsa_jwk(variant.into(), &jwk, policy);
2291        mint(accessor, material).await
2292    }
2293
2294    async fn unwrap_signing_key_pkcs8(
2295        accessor: &Accessor<T, Self>,
2296        variant: ecdsa_verify_iface::EcdsaVariant,
2297        input: Resource<UnwrapInput>,
2298        options: Resource<crate::SigningKeyOptions>,
2299    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2300        let policy = take_options(accessor, options).await?.policy;
2301        let input = take_options(accessor, input).await?.material;
2302        let material =
2303            polymorph_webcrypto_core::unwrap_ecdsa_signing_key_pkcs8(variant.into(), input, policy);
2304        mint(accessor, material).await
2305    }
2306
2307    async fn unwrap_signing_key_jwk(
2308        accessor: &Accessor<T, Self>,
2309        variant: ecdsa_verify_iface::EcdsaVariant,
2310        input: Resource<UnwrapInput>,
2311        options: Resource<crate::SigningKeyOptions>,
2312    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2313        let policy = take_options(accessor, options).await?.policy;
2314        let input = take_options(accessor, input).await?.material;
2315        let material =
2316            polymorph_webcrypto_core::unwrap_ecdsa_signing_key_jwk(variant.into(), input, policy);
2317        mint(accessor, material).await
2318    }
2319}
2320
2321// --- rsa (key minting) -----------------------------------------------------------
2322
2323impl rsa_iface::Host for WasiWebcryptoCtxView<'_> {}
2324impl rsassa_verify_iface::Host for WasiWebcryptoCtxView<'_> {}
2325impl rsa_pss_verify_iface::Host for WasiWebcryptoCtxView<'_> {}
2326impl rsassa_sign_iface::Host for WasiWebcryptoCtxView<'_> {}
2327impl rsa_pss_sign_iface::Host for WasiWebcryptoCtxView<'_> {}
2328
2329/// The WIT `rsa.rsa-modulus` cases, converted locally: the type is
2330/// deliberately outside the shared core's `impl_conversions!` (see its
2331/// doc in the core).
2332impl From<rsa_iface::RsaModulus> for polymorph_webcrypto_core::RsaModulus {
2333    fn from(modulus: rsa_iface::RsaModulus) -> Self {
2334        match modulus {
2335            rsa_iface::RsaModulus::M2048 => Self::M2048,
2336            rsa_iface::RsaModulus::M3072 => Self::M3072,
2337            rsa_iface::RsaModulus::M4096 => Self::M4096,
2338            rsa_iface::RsaModulus::M8192 => Self::M8192,
2339        }
2340    }
2341}
2342
2343impl<T: Send> rsassa_verify_iface::HostWithStore<T> for WasiWebcrypto {
2344    async fn import_verifying_key_spki(
2345        accessor: &Accessor<T, Self>,
2346        variant: rsa_iface::RsaVariant,
2347        spki: Vec<u8>,
2348    ) -> Result<std::result::Result<Resource<VerifyingKey>, Error>> {
2349        let public = SigPublic::import_rsassa_spki(variant.into(), &spki);
2350        mint(accessor, public).await
2351    }
2352
2353    async fn import_verifying_key_jwk(
2354        accessor: &Accessor<T, Self>,
2355        variant: rsa_iface::RsaVariant,
2356        jwk: String,
2357    ) -> Result<std::result::Result<Resource<VerifyingKey>, Error>> {
2358        let public = SigPublic::import_rsassa_jwk(variant.into(), &jwk);
2359        mint(accessor, public).await
2360    }
2361}
2362
2363impl<T: Send> rsa_pss_verify_iface::HostWithStore<T> for WasiWebcrypto {
2364    async fn import_verifying_key_spki(
2365        accessor: &Accessor<T, Self>,
2366        variant: rsa_iface::RsaVariant,
2367        salt_length: u32,
2368        spki: Vec<u8>,
2369    ) -> Result<std::result::Result<Resource<VerifyingKey>, Error>> {
2370        let public = SigPublic::import_pss_spki(variant.into(), salt_length, &spki);
2371        mint(accessor, public).await
2372    }
2373
2374    async fn import_verifying_key_jwk(
2375        accessor: &Accessor<T, Self>,
2376        variant: rsa_iface::RsaVariant,
2377        salt_length: u32,
2378        jwk: String,
2379    ) -> Result<std::result::Result<Resource<VerifyingKey>, Error>> {
2380        let public = SigPublic::import_pss_jwk(variant.into(), salt_length, &jwk);
2381        mint(accessor, public).await
2382    }
2383}
2384
2385impl<T: Send> rsassa_sign_iface::HostWithStore<T> for WasiWebcrypto {
2386    async fn generate_key(
2387        accessor: &Accessor<T, Self>,
2388        variant: rsa_iface::RsaVariant,
2389        modulus: rsa_iface::RsaModulus,
2390        options: Resource<crate::SigningKeyOptions>,
2391    ) -> Result<std::result::Result<(Resource<SigningKey>, Resource<VerifyingKey>), Error>> {
2392        let policy = take_options(accessor, options).await?.policy;
2393        let material =
2394            match SigningKeyMaterial::generate_rsassa(variant.into(), modulus.into(), policy)
2395                .map_err(rng_trap("random key generation"))?
2396            {
2397                Ok(material) => material,
2398                Err(err) => return Ok(Err(err.into())),
2399            };
2400        mint_signing_pair(accessor, material).await
2401    }
2402
2403    async fn import_signing_key_pkcs8(
2404        accessor: &Accessor<T, Self>,
2405        variant: rsa_iface::RsaVariant,
2406        pkcs8: Vec<u8>,
2407        options: Resource<crate::SigningKeyOptions>,
2408    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2409        let policy = take_options(accessor, options).await?.policy;
2410        let material = SigningKeyMaterial::import_rsassa_pkcs8(variant.into(), &pkcs8, policy);
2411        mint(accessor, material).await
2412    }
2413
2414    async fn import_signing_key_jwk(
2415        accessor: &Accessor<T, Self>,
2416        variant: rsa_iface::RsaVariant,
2417        jwk: String,
2418        options: Resource<crate::SigningKeyOptions>,
2419    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2420        let policy = take_options(accessor, options).await?.policy;
2421        let material = SigningKeyMaterial::import_rsassa_jwk(variant.into(), &jwk, policy);
2422        mint(accessor, material).await
2423    }
2424
2425    async fn unwrap_signing_key_pkcs8(
2426        accessor: &Accessor<T, Self>,
2427        variant: rsa_iface::RsaVariant,
2428        input: Resource<UnwrapInput>,
2429        options: Resource<crate::SigningKeyOptions>,
2430    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2431        let policy = take_options(accessor, options).await?.policy;
2432        let input = take_options(accessor, input).await?.material;
2433        let material = polymorph_webcrypto_core::unwrap_rsassa_signing_key_pkcs8(
2434            variant.into(),
2435            input,
2436            policy,
2437        );
2438        mint(accessor, material).await
2439    }
2440
2441    async fn unwrap_signing_key_jwk(
2442        accessor: &Accessor<T, Self>,
2443        variant: rsa_iface::RsaVariant,
2444        input: Resource<UnwrapInput>,
2445        options: Resource<crate::SigningKeyOptions>,
2446    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2447        let policy = take_options(accessor, options).await?.policy;
2448        let input = take_options(accessor, input).await?.material;
2449        let material =
2450            polymorph_webcrypto_core::unwrap_rsassa_signing_key_jwk(variant.into(), input, policy);
2451        mint(accessor, material).await
2452    }
2453}
2454
2455impl<T: Send> rsa_pss_sign_iface::HostWithStore<T> for WasiWebcrypto {
2456    async fn generate_key(
2457        accessor: &Accessor<T, Self>,
2458        variant: rsa_iface::RsaVariant,
2459        modulus: rsa_iface::RsaModulus,
2460        options: Resource<crate::SigningKeyOptions>,
2461    ) -> Result<std::result::Result<(Resource<SigningKey>, Resource<VerifyingKey>), Error>> {
2462        let policy = take_options(accessor, options).await?.policy;
2463        let material =
2464            match SigningKeyMaterial::generate_pss(variant.into(), modulus.into(), policy)
2465                .map_err(rng_trap("random key generation"))?
2466            {
2467                Ok(material) => material,
2468                Err(err) => return Ok(Err(err.into())),
2469            };
2470        mint_signing_pair(accessor, material).await
2471    }
2472
2473    async fn import_signing_key_pkcs8(
2474        accessor: &Accessor<T, Self>,
2475        variant: rsa_iface::RsaVariant,
2476        pkcs8: Vec<u8>,
2477        options: Resource<crate::SigningKeyOptions>,
2478    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2479        let policy = take_options(accessor, options).await?.policy;
2480        let material = SigningKeyMaterial::import_pss_pkcs8(variant.into(), &pkcs8, policy);
2481        mint(accessor, material).await
2482    }
2483
2484    async fn import_signing_key_jwk(
2485        accessor: &Accessor<T, Self>,
2486        variant: rsa_iface::RsaVariant,
2487        jwk: String,
2488        options: Resource<crate::SigningKeyOptions>,
2489    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2490        let policy = take_options(accessor, options).await?.policy;
2491        let material = SigningKeyMaterial::import_pss_jwk(variant.into(), &jwk, policy);
2492        mint(accessor, material).await
2493    }
2494
2495    async fn unwrap_signing_key_pkcs8(
2496        accessor: &Accessor<T, Self>,
2497        variant: rsa_iface::RsaVariant,
2498        input: Resource<UnwrapInput>,
2499        options: Resource<crate::SigningKeyOptions>,
2500    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2501        let policy = take_options(accessor, options).await?.policy;
2502        let input = take_options(accessor, input).await?.material;
2503        let material =
2504            polymorph_webcrypto_core::unwrap_pss_signing_key_pkcs8(variant.into(), input, policy);
2505        mint(accessor, material).await
2506    }
2507
2508    async fn unwrap_signing_key_jwk(
2509        accessor: &Accessor<T, Self>,
2510        variant: rsa_iface::RsaVariant,
2511        input: Resource<UnwrapInput>,
2512        options: Resource<crate::SigningKeyOptions>,
2513    ) -> Result<std::result::Result<Resource<SigningKey>, Error>> {
2514        let policy = take_options(accessor, options).await?.policy;
2515        let input = take_options(accessor, input).await?.material;
2516        let material =
2517            polymorph_webcrypto_core::unwrap_pss_signing_key_jwk(variant.into(), input, policy);
2518        mint(accessor, material).await
2519    }
2520}
2521
2522// --- public-encryption -----------------------------------------------------------
2523
2524impl public_encryption_iface::Host for WasiWebcryptoCtxView<'_> {}
2525
2526host_options! {
2527    public_encryption_iface::{HostDecryptionKeyOptions, HostDecryptionKeyOptionsWithStore}
2528    for crate::DecryptionKeyOptions {
2529        can_decrypt => decrypt,
2530        can_unwrap => unwrap,
2531        extractable => extractable,
2532    }
2533}
2534
2535impl HostEncryptionKey for WasiWebcryptoCtxView<'_> {
2536    fn algorithm_name(&mut self, self_: Resource<EncryptionKey>) -> Result<String> {
2537        Ok(self.table.get(&self_)?.material.name().to_string())
2538    }
2539
2540    fn algorithm_hash(&mut self, self_: Resource<EncryptionKey>) -> Result<Option<String>> {
2541        Ok(self.table.get(&self_)?.material.hash().map(str::to_string))
2542    }
2543
2544    fn algorithm_length(&mut self, self_: Resource<EncryptionKey>) -> Result<Option<u32>> {
2545        Ok(self.table.get(&self_)?.material.length())
2546    }
2547
2548    fn algorithm_public_exponent(
2549        &mut self,
2550        self_: Resource<EncryptionKey>,
2551    ) -> Result<Option<Vec<u8>>> {
2552        Ok(self.table.get(&self_)?.material.public_exponent())
2553    }
2554}
2555
2556impl<T: Send> HostEncryptionKeyWithStore<T> for WasiWebcrypto {
2557    async fn encrypt(
2558        accessor: &Accessor<T, Self>,
2559        self_: Resource<EncryptionKey>,
2560        label: Option<Vec<u8>>,
2561        plaintext: Vec<u8>,
2562    ) -> Result<std::result::Result<Vec<u8>, Error>> {
2563        with_resource(accessor, self_, |key| {
2564            key.material
2565                .encrypt(label.as_deref(), &plaintext)
2566                .map_err(Error::from)
2567        })
2568        .await
2569    }
2570
2571    async fn wrap(
2572        accessor: &Accessor<T, Self>,
2573        self_: Resource<EncryptionKey>,
2574        label: Option<Vec<u8>>,
2575        input: Resource<WrapInput>,
2576    ) -> Result<std::result::Result<Vec<u8>, Error>> {
2577        let input = take_options(accessor, input).await?.material;
2578        with_resource(accessor, self_, |key| {
2579            key.material
2580                .wrap(label.as_deref(), input)
2581                .map_err(Error::from)
2582        })
2583        .await
2584    }
2585
2586    async fn export_key_raw(
2587        accessor: &Accessor<T, Self>,
2588        self_: Resource<EncryptionKey>,
2589    ) -> Result<std::result::Result<Vec<u8>, Error>> {
2590        with_resource(accessor, self_, |key| {
2591            key.material.export().map_err(Error::from)
2592        })
2593        .await
2594    }
2595
2596    async fn export_key_spki(
2597        accessor: &Accessor<T, Self>,
2598        self_: Resource<EncryptionKey>,
2599    ) -> Result<std::result::Result<Vec<u8>, Error>> {
2600        with_resource(accessor, self_, |key| Ok(key.material.export_spki())).await
2601    }
2602
2603    async fn export_key_jwk(
2604        accessor: &Accessor<T, Self>,
2605        self_: Resource<EncryptionKey>,
2606    ) -> Result<std::result::Result<String, Error>> {
2607        with_resource(accessor, self_, |key| Ok(key.material.export_jwk())).await
2608    }
2609
2610    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<EncryptionKey>) -> Result<()> {
2611        drop_resource(accessor, rep).await
2612    }
2613}
2614
2615impl HostDecryptionKey for WasiWebcryptoCtxView<'_> {
2616    fn algorithm_name(&mut self, self_: Resource<DecryptionKey>) -> Result<String> {
2617        Ok(self.table.get(&self_)?.material.name().to_string())
2618    }
2619
2620    fn algorithm_hash(&mut self, self_: Resource<DecryptionKey>) -> Result<Option<String>> {
2621        Ok(self.table.get(&self_)?.material.hash().map(str::to_string))
2622    }
2623
2624    fn algorithm_length(&mut self, self_: Resource<DecryptionKey>) -> Result<Option<u32>> {
2625        Ok(self.table.get(&self_)?.material.length())
2626    }
2627
2628    fn algorithm_public_exponent(
2629        &mut self,
2630        self_: Resource<DecryptionKey>,
2631    ) -> Result<Option<Vec<u8>>> {
2632        Ok(self.table.get(&self_)?.material.public_exponent())
2633    }
2634
2635    fn can_decrypt(&mut self, self_: Resource<DecryptionKey>) -> Result<bool> {
2636        Ok(self.table.get(&self_)?.material.can_decrypt())
2637    }
2638
2639    fn can_unwrap(&mut self, self_: Resource<DecryptionKey>) -> Result<bool> {
2640        Ok(self.table.get(&self_)?.material.can_unwrap())
2641    }
2642
2643    fn extractable(&mut self, self_: Resource<DecryptionKey>) -> Result<bool> {
2644        Ok(self.table.get(&self_)?.material.extractable())
2645    }
2646}
2647
2648impl<T: Send> HostDecryptionKeyWithStore<T> for WasiWebcrypto {
2649    async fn decrypt(
2650        accessor: &Accessor<T, Self>,
2651        self_: Resource<DecryptionKey>,
2652        label: Option<Vec<u8>>,
2653        ciphertext: Vec<u8>,
2654    ) -> Result<std::result::Result<Vec<u8>, Error>> {
2655        with_resource(accessor, self_, |key| {
2656            key.material
2657                .decrypt(label.as_deref(), &ciphertext)
2658                .map_err(Error::from)
2659        })
2660        .await
2661    }
2662
2663    async fn unwrap(
2664        accessor: &Accessor<T, Self>,
2665        self_: Resource<DecryptionKey>,
2666        label: Option<Vec<u8>>,
2667        ciphertext: Vec<u8>,
2668    ) -> Result<std::result::Result<Resource<UnwrapInput>, Error>> {
2669        let material = with_resource(accessor, self_, |key| {
2670            key.material.unwrap(label.as_deref(), &ciphertext)
2671        })
2672        .await?;
2673        mint(accessor, material).await
2674    }
2675
2676    async fn export_key_jwk(
2677        accessor: &Accessor<T, Self>,
2678        self_: Resource<DecryptionKey>,
2679    ) -> Result<std::result::Result<String, Error>> {
2680        with_resource(accessor, self_, |key| {
2681            key.material.export_jwk().map_err(Error::from)
2682        })
2683        .await
2684    }
2685
2686    async fn export_key_pkcs8(
2687        accessor: &Accessor<T, Self>,
2688        self_: Resource<DecryptionKey>,
2689    ) -> Result<std::result::Result<Vec<u8>, Error>> {
2690        with_resource(accessor, self_, |key| {
2691            key.material.export_pkcs8().map_err(Error::from)
2692        })
2693        .await
2694    }
2695
2696    async fn to_wrap_input_jwk(
2697        accessor: &Accessor<T, Self>,
2698        self_: Resource<DecryptionKey>,
2699    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
2700        to_wrap_input(accessor, self_, WrapFormat::Jwk, |key| {
2701            key.material.export_jwk().map(String::into_bytes)
2702        })
2703        .await
2704    }
2705
2706    async fn to_wrap_input_pkcs8(
2707        accessor: &Accessor<T, Self>,
2708        self_: Resource<DecryptionKey>,
2709    ) -> Result<std::result::Result<Resource<WrapInput>, Error>> {
2710        to_wrap_input(accessor, self_, WrapFormat::Pkcs8, |key| {
2711            key.material.export_pkcs8()
2712        })
2713        .await
2714    }
2715
2716    async fn drop(accessor: &Accessor<T, Self>, rep: Resource<DecryptionKey>) -> Result<()> {
2717        drop_resource(accessor, rep).await
2718    }
2719}
2720
2721// --- rsa-oaep (key minting) --------------------------------------------------------
2722
2723impl rsa_oaep_encrypt_iface::Host for WasiWebcryptoCtxView<'_> {}
2724impl rsa_oaep_decrypt_iface::Host for WasiWebcryptoCtxView<'_> {}
2725
2726impl<T: Send> rsa_oaep_encrypt_iface::HostWithStore<T> for WasiWebcrypto {
2727    async fn import_encryption_key_spki(
2728        accessor: &Accessor<T, Self>,
2729        variant: rsa_iface::RsaVariant,
2730        spki: Vec<u8>,
2731    ) -> Result<std::result::Result<Resource<EncryptionKey>, Error>> {
2732        let material = EncryptionKeyMaterial::import_oaep_spki(variant.into(), &spki);
2733        mint(accessor, material).await
2734    }
2735
2736    async fn import_encryption_key_jwk(
2737        accessor: &Accessor<T, Self>,
2738        variant: rsa_iface::RsaVariant,
2739        jwk: String,
2740    ) -> Result<std::result::Result<Resource<EncryptionKey>, Error>> {
2741        let material = EncryptionKeyMaterial::import_oaep_jwk(variant.into(), &jwk);
2742        mint(accessor, material).await
2743    }
2744}
2745
2746impl<T: Send> rsa_oaep_decrypt_iface::HostWithStore<T> for WasiWebcrypto {
2747    async fn generate_key(
2748        accessor: &Accessor<T, Self>,
2749        variant: rsa_iface::RsaVariant,
2750        modulus: rsa_iface::RsaModulus,
2751        options: Resource<crate::DecryptionKeyOptions>,
2752    ) -> Result<std::result::Result<(Resource<DecryptionKey>, Resource<EncryptionKey>), Error>>
2753    {
2754        let policy = take_options(accessor, options).await?.policy;
2755        let material =
2756            match DecryptionKeyMaterial::generate_oaep(variant.into(), modulus.into(), policy)
2757                .map_err(rng_trap("random key generation"))?
2758            {
2759                Ok(material) => material,
2760                Err(err) => return Ok(Err(err.into())),
2761            };
2762        let public = material.public();
2763        mint_key_pair(accessor, material, public).await
2764    }
2765
2766    async fn import_decryption_key_pkcs8(
2767        accessor: &Accessor<T, Self>,
2768        variant: rsa_iface::RsaVariant,
2769        pkcs8: Vec<u8>,
2770        options: Resource<crate::DecryptionKeyOptions>,
2771    ) -> Result<std::result::Result<Resource<DecryptionKey>, Error>> {
2772        let policy = take_options(accessor, options).await?.policy;
2773        let material = DecryptionKeyMaterial::import_oaep_pkcs8(variant.into(), &pkcs8, policy);
2774        mint(accessor, material).await
2775    }
2776
2777    async fn import_decryption_key_jwk(
2778        accessor: &Accessor<T, Self>,
2779        variant: rsa_iface::RsaVariant,
2780        jwk: String,
2781        options: Resource<crate::DecryptionKeyOptions>,
2782    ) -> Result<std::result::Result<Resource<DecryptionKey>, Error>> {
2783        let policy = take_options(accessor, options).await?.policy;
2784        let material = DecryptionKeyMaterial::import_oaep_jwk(variant.into(), &jwk, policy);
2785        mint(accessor, material).await
2786    }
2787
2788    async fn unwrap_decryption_key_pkcs8(
2789        accessor: &Accessor<T, Self>,
2790        variant: rsa_iface::RsaVariant,
2791        input: Resource<UnwrapInput>,
2792        options: Resource<crate::DecryptionKeyOptions>,
2793    ) -> Result<std::result::Result<Resource<DecryptionKey>, Error>> {
2794        let policy = take_options(accessor, options).await?.policy;
2795        let input = take_options(accessor, input).await?.material;
2796        let material = polymorph_webcrypto_core::unwrap_oaep_decryption_key_pkcs8(
2797            variant.into(),
2798            input,
2799            policy,
2800        );
2801        mint(accessor, material).await
2802    }
2803
2804    async fn unwrap_decryption_key_jwk(
2805        accessor: &Accessor<T, Self>,
2806        variant: rsa_iface::RsaVariant,
2807        input: Resource<UnwrapInput>,
2808        options: Resource<crate::DecryptionKeyOptions>,
2809    ) -> Result<std::result::Result<Resource<DecryptionKey>, Error>> {
2810        let policy = take_options(accessor, options).await?.policy;
2811        let input = take_options(accessor, input).await?.material;
2812        let material =
2813            polymorph_webcrypto_core::unwrap_oaep_decryption_key_jwk(variant.into(), input, policy);
2814        mint(accessor, material).await
2815    }
2816}
2817
2818#[cfg(test)]
2819mod tests {
2820    use crate::bindings::webcrypto::sha1_checked::Host as _;
2821    use crate::bindings::webcrypto::sha2::{self as sha2_iface, Host as _};
2822    use crate::bindings::webcrypto::types::Error;
2823    use crate::{MacKey, Minted as _};
2824    use polymorph_webcrypto_core::{MacKeyMaterial, MacPolicy, Sha2Variant};
2825
2826    /// Minting charges the retention budget: a spent budget fails a
2827    /// fallible mint with the WIT's operational error and traps an options
2828    /// constructor (whose signature has no error channel), and dropping a
2829    /// held resource readmits the next mint.
2830    #[test]
2831    fn minting_charges_the_retention_budget() {
2832        let mut ctx = crate::WasiWebcryptoCtx::new();
2833        ctx.set_retention_limit(Some(crate::limits::RETENTION_FLOOR));
2834        let mut table = wasmtime::component::ResourceTable::new();
2835        let mut view = crate::WasiWebcryptoCtxView {
2836            ctx: &mut ctx,
2837            table: &mut table,
2838        };
2839
2840        let digest = view
2841            .make_digest(sha2_iface::Sha2Variant::Sha256)
2842            .unwrap()
2843            .expect("one floor-sized mint fits the budget");
2844
2845        match view.make_rejecting_digest().unwrap() {
2846            Err(Error::Other(msg)) => assert!(msg.contains("set_retention_limit"), "{msg}"),
2847            other => panic!("expected the retention error, got {other:?}"),
2848        }
2849        let trap = crate::bindings::webcrypto::mac::HostMacKeyOptions::new(&mut view)
2850            .expect_err("an options mint past the budget traps");
2851        assert!(trap.to_string().contains("set_retention_limit"), "{trap}");
2852
2853        view.table.delete(digest).unwrap();
2854        view.make_rejecting_digest()
2855            .unwrap()
2856            .expect("the dropped resource's charge readmits the mint");
2857    }
2858
2859    /// `Debug` on key-holding types never prints key material: the bytes
2860    /// are redacted (in the shared core's material types, which these
2861    /// resource types derive through), so a key reaching a log line cannot
2862    /// leak.
2863    #[test]
2864    fn debug_redacts_key_material() {
2865        let policy = MacPolicy {
2866            sign: true,
2867            verify: true,
2868            extractable: true,
2869        };
2870        let pool = crate::limits::pool(1024);
2871        let key = MacKey::minted(
2872            MacKeyMaterial::import(Sha2Variant::Sha256, vec![0xAB; 32], policy).unwrap(),
2873            crate::limits::charge(&pool, 32).unwrap(),
2874        );
2875        let rendered = format!("{key:?}");
2876        assert!(rendered.contains("<redacted>"), "{rendered}");
2877        assert!(!rendered.contains("171"), "{rendered}"); // 0xAB
2878        assert!(!rendered.to_lowercase().contains("ab, ab"), "{rendered}");
2879    }
2880}