Skip to main content

polymorph_webcrypto_guest/
lib.rs

1//! Guest-side bindings and ergonomic helpers for the `polymorph:webcrypto`
2//! interfaces.
3//!
4//! This crate is the intended way for Rust guest components to *consume*
5//! `polymorph:webcrypto`: it binds the whole import surface once (the
6//! [`bindings`] module) and wraps the key resources in newtypes whose
7//! operations take a [`DataSource`] — a byte slice, an owned buffer, or a
8//! component-model stream — so callers need none of the stream plumbing the
9//! interfaces are defined in terms of.
10//!
11//! Most consumers need **no `polymorph:webcrypto` WIT at all**: link this crate
12//! and call it, and the componentized binary imports exactly the interfaces
13//! it uses (unused imports are stripped). Only list the imports in your own
14//! world — remapping them onto this crate's [`bindings`] modules with
15//! wit-bindgen's `with:` option — if your own interfaces name these types or
16//! external tooling validates your world's shape. Do **not** bind the same
17//! interfaces with a second `generate!` without that remapping: the two
18//! expansions would produce distinct, unconvertible resource types, and the
19//! newtypes here wrap only this crate's generation.
20//!
21//! # Cargo features
22//!
23//! - `bytes`: `DataSource::from_buf` feeds an operation from any
24//!   `bytes::Buf`, chunk by chunk.
25//! - `futures-io`: `DataSource::from_reader` feeds an operation from any
26//!   `futures_io::AsyncRead`; read failures surface as [`Error::Read`].
27//!
28//! # Contract notes carried over from the WIT
29//!
30//! - **The wrappers hide streams, not the closure rule.** An operation's
31//!   input stream ends no later than the operation completes, and only a
32//!   failing operation may end it early; these helpers feed the source and
33//!   await the result concurrently, reporting the operation's error over
34//!   the feed's fate, so that contract is invisible here. Callers with
35//!   needs beyond [`DataSource`] use the
36//!   [`bindings`] resources directly with wit-bindgen's own stream
37//!   primitives ([`wit_stream::new`], `StreamWriter::write_all`,
38//!   [`StreamReader::collect`]).
39//! - **Writer drop ends the message.** A stream's producer failing midway
40//!   is indistinguishable from it finishing (the ABI carries no verdict at
41//!   end-of-stream). Buffer-backed [`DataSource`]s own their whole input, so
42//!   this only concerns stream-backed sources; see
43//!   [`DataSource`]'s truncating-producer warning.
44//! - **Implementations may bound input sizes.** Hosts enforce buffering
45//!   limits as recoverable [`Error::Other`] values (see the WIT
46//!   `types.error` docs); nothing here retries or special-cases them.
47//! - **Nonces are the caller's problem on `aead`.** [`Aead::seal`] leaves
48//!   nonce uniqueness per key entirely to you, and nonce reuse under one
49//!   key defeats the algorithm's guarantees.
50
51#![deny(missing_docs)]
52
53use std::borrow::Cow;
54use std::fmt;
55
56use wit_bindgen::StreamWriter;
57
58/// Re-export of the `wit-bindgen` crate this crate's bindings were generated
59/// with, so consumers can name its runtime types (streams, futures) without
60/// depending on — and version-matching — `wit-bindgen` themselves.
61pub use wit_bindgen;
62/// The component-model byte-stream reader, as returned by [`Aead::seal`] and
63/// friends and accepted by [`DataSource`].
64pub use wit_bindgen::StreamReader;
65
66mod generated {
67    #![allow(missing_docs)]
68    // One mutually exclusive expansion per cargo-feature combination
69    // rather than one parameterized invocation: `generate!`'s `features`
70    // list is static, and the arms must stay option-for-option identical
71    // apart from it. This scales as 2^n in the gated cargo features — at
72    // n where this stops being tolerable, the bindings move to a build
73    // script that computes the flag list (tracked with the SDK's other
74    // cargo-feature debt in #85).
75    #[cfg(all(
76        feature = "sha1-checked",
77        feature = "rsa-sign",
78        feature = "rsa-oaep-decrypt"
79    ))]
80    wit_bindgen::generate!({
81        path: "wit",
82        features: ["sha1-checked", "rsa-sign", "rsa-oaep-decrypt"],
83        world: "imports",
84        generate_all,
85        pub_export_macro: false,
86    });
87    #[cfg(all(
88        feature = "sha1-checked",
89        feature = "rsa-sign",
90        not(feature = "rsa-oaep-decrypt")
91    ))]
92    wit_bindgen::generate!({
93        path: "wit",
94        features: ["sha1-checked", "rsa-sign"],
95        world: "imports",
96        generate_all,
97        pub_export_macro: false,
98    });
99    #[cfg(all(
100        feature = "sha1-checked",
101        not(feature = "rsa-sign"),
102        feature = "rsa-oaep-decrypt"
103    ))]
104    wit_bindgen::generate!({
105        path: "wit",
106        features: ["sha1-checked", "rsa-oaep-decrypt"],
107        world: "imports",
108        generate_all,
109        pub_export_macro: false,
110    });
111    #[cfg(all(
112        feature = "sha1-checked",
113        not(feature = "rsa-sign"),
114        not(feature = "rsa-oaep-decrypt")
115    ))]
116    wit_bindgen::generate!({
117        path: "wit",
118        features: ["sha1-checked"],
119        world: "imports",
120        generate_all,
121        pub_export_macro: false,
122    });
123    #[cfg(all(
124        not(feature = "sha1-checked"),
125        feature = "rsa-sign",
126        feature = "rsa-oaep-decrypt"
127    ))]
128    wit_bindgen::generate!({
129        path: "wit",
130        features: ["rsa-sign", "rsa-oaep-decrypt"],
131        world: "imports",
132        generate_all,
133        pub_export_macro: false,
134    });
135    #[cfg(all(
136        not(feature = "sha1-checked"),
137        feature = "rsa-sign",
138        not(feature = "rsa-oaep-decrypt")
139    ))]
140    wit_bindgen::generate!({
141        path: "wit",
142        features: ["rsa-sign"],
143        world: "imports",
144        generate_all,
145        pub_export_macro: false,
146    });
147    #[cfg(all(
148        not(feature = "sha1-checked"),
149        not(feature = "rsa-sign"),
150        feature = "rsa-oaep-decrypt"
151    ))]
152    wit_bindgen::generate!({
153        path: "wit",
154        features: ["rsa-oaep-decrypt"],
155        world: "imports",
156        generate_all,
157        pub_export_macro: false,
158    });
159    #[cfg(all(
160        not(feature = "sha1-checked"),
161        not(feature = "rsa-sign"),
162        not(feature = "rsa-oaep-decrypt")
163    ))]
164    wit_bindgen::generate!({
165        path: "wit",
166        world: "imports",
167        generate_all,
168        pub_export_macro: false,
169    });
170}
171
172/// The generated bindings for the full `polymorph:webcrypto` import surface.
173///
174/// The newtype wrappers cover the common cases; these are the escape hatch
175/// for callers driving the streams themselves and for passing resources
176/// through a consumer's own interfaces (via [`Mac::into_raw`] and friends).
177pub mod bindings {
178    // `aes`, `rsa`, and `sha2` are here for their *types*: they define
179    // `aes-variant`, `rsa-variant`, and `sha2-variant`, which the minting
180    // interfaces only alias, and rustdoc renders an alias into a private
181    // module as an empty enum.
182    #[cfg(feature = "rsa-oaep-decrypt")]
183    pub use super::generated::polymorph::webcrypto::rsa_oaep_decrypt;
184    #[cfg(feature = "sha1-checked")]
185    pub use super::generated::polymorph::webcrypto::sha1_checked;
186    pub use super::generated::polymorph::webcrypto::{
187        aead, aes, aes_cbc, aes_ctr, aes_gcm, aes_kw, cipher, derivation, digest, ecdh, ecdsa_sign,
188        ecdsa_verify, ed25519_sign, ed25519_verify, hkdf, hkdf_sha1, hkdf_sha2, hmac_sha1,
189        hmac_sha2, key_agreement, key_wrap, mac, pbkdf2, pbkdf2_sha1, pbkdf2_sha2,
190        public_encryption, rsa, rsa_oaep_encrypt, rsa_pss_verify, rsassa_pkcs1_v15_verify, sha2,
191        signature, types, wrapping, x25519,
192    };
193    #[cfg(feature = "rsa-sign")]
194    pub use super::generated::polymorph::webcrypto::{rsa_pss_sign, rsassa_pkcs1_v15_sign};
195}
196
197pub use generated::wit_stream;
198
199// --- error ---------------------------------------------------------------------
200
201/// Errors surfaced by key creation and cryptographic operations.
202///
203/// Mirrors the WIT `types.error` variant (see the doc comments in
204/// `wit/webcrypto.wit` for the full contracts), plus [`Error::Read`] for
205/// failures of a caller-supplied [`DataSource`] producer. Misuse of the API
206/// is unrepresentable by construction — operations are one-shot calls on
207/// immutable key resources — and so has no variant here.
208///
209/// `#[non_exhaustive]`: this enum carries [`Error::Read`] in addition to the
210/// WIT cases, so it grows independently of the package's own rule that a new
211/// `types.error` case is semver-major. The `From` conversion below is
212/// exhaustive over the WIT variant, so a case added there is a compile error
213/// here rather than a silent fallthrough.
214#[derive(Debug)]
215#[non_exhaustive]
216pub enum Error {
217    /// The supplied key material is invalid for the algorithm (for example,
218    /// a wrong-length raw key, or one rejected by an implementation's
219    /// key-length policy). The string is human-readable.
220    InvalidKey(String),
221    /// The supplied nonce is invalid for the algorithm (for example, a
222    /// wrong-length AES-GCM nonce). The string is human-readable.
223    InvalidNonce(String),
224    /// Verification failed: the MAC tag, the signature, or the ciphertext or
225    /// its associated data did not verify under the key. Deliberately
226    /// carries no detail, so implementations cannot leak *why* verification
227    /// failed.
228    AuthenticationFailed,
229    /// The key was created with `extractable` false, so its material cannot
230    /// be exported.
231    NotExtractable,
232    /// The request was well-formed, but the implementation does not serve
233    /// the requested algorithm parameters. The string is human-readable.
234    Unsupported(String),
235    /// The key does not permit the requested operation: it was minted (or
236    /// arrived from a platform keystore) with the operation's usage
237    /// disabled. The string names the refused operation.
238    NotPermitted(String),
239    /// An implementation-specific operational failure (an external keystore
240    /// that cannot complete the operation, an input exceeding a buffering
241    /// limit, …). The string is human-readable.
242    Other(String),
243    /// A named condition outside the WIT `error` variant's closed set,
244    /// identified by the (`origin`, `name`) pair — the only branchable
245    /// identity; `message` is human-readable prose, never contract.
246    /// Handle an unrecognized pair as [`Error::Other`]. Known pairs have
247    /// constants in [`extension`].
248    Extension(bindings::types::ExtensionError),
249    /// A caller-supplied [`DataSource`] producer failed while being fed into
250    /// the operation (see `DataSource::from_reader`). The operation's own
251    /// result is discarded: it was computed over a truncated input.
252    Read(std::io::Error),
253    /// The operation succeeded without accepting the whole source: the
254    /// provider closed the stream's read end early and then reported
255    /// success. The stream-closure rule permits ending the input early
256    /// only when the operation fails (a failing operation's own error is
257    /// what these wrappers report), so this indicates a defective
258    /// provider, not a condition to retry.
259    ShortWrite,
260}
261
262/// The known extension-error conditions, as (`origin`, `name`) constants
263/// for matching against [`Error::Extension`].
264pub mod extension {
265    /// The `origin` of conditions the `polymorph:webcrypto` package defines.
266    pub const LANN_WEBCRYPTO: &str = "polymorph:webcrypto";
267    /// `sha1-checked`'s collision condition: a rejecting digest's input
268    /// carried a SHA-1 collision attack pattern.
269    pub const COLLISION_DETECTED: &str = "collision-detected";
270}
271
272impl From<bindings::types::Error> for Error {
273    fn from(error: bindings::types::Error) -> Self {
274        use bindings::types::Error as Raw;
275        match error {
276            Raw::InvalidKey(detail) => Error::InvalidKey(detail),
277            Raw::InvalidNonce(detail) => Error::InvalidNonce(detail),
278            Raw::AuthenticationFailed => Error::AuthenticationFailed,
279            Raw::NotExtractable => Error::NotExtractable,
280            Raw::Unsupported(detail) => Error::Unsupported(detail),
281            Raw::NotPermitted(detail) => Error::NotPermitted(detail),
282            Raw::Other(detail) => Error::Other(detail),
283            Raw::Extension(ext) => Error::Extension(ext),
284        }
285    }
286}
287
288/// Renders the WIT cases case-name-first — `invalid-key: <detail>` — plus
289/// the [`Error::Read`] case this type adds.
290impl fmt::Display for Error {
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        match self {
293            Error::InvalidKey(detail) => write!(f, "invalid-key: {detail}"),
294            Error::InvalidNonce(detail) => write!(f, "invalid-nonce: {detail}"),
295            Error::AuthenticationFailed => write!(f, "authentication-failed"),
296            Error::NotExtractable => write!(f, "not-extractable"),
297            Error::Unsupported(detail) => write!(f, "unsupported: {detail}"),
298            Error::NotPermitted(detail) => write!(f, "not-permitted: {detail}"),
299            Error::Other(detail) => write!(f, "other: {detail}"),
300            Error::Extension(ext) => write!(
301                f,
302                "extension({origin}, {name}): {message}",
303                origin = ext.origin,
304                name = ext.name,
305                message = ext.message,
306            ),
307            Error::Read(error) => write!(f, "data source read failed: {error}"),
308            Error::ShortWrite => write!(
309                f,
310                "short write: the operation stopped accepting input before the \
311                 source was fully written"
312            ),
313        }
314    }
315}
316
317impl std::error::Error for Error {
318    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
319        match self {
320            Error::Read(error) => Some(error),
321            _ => None,
322        }
323    }
324}
325
326// --- data sources ----------------------------------------------------------------
327
328/// The input to a wrapped operation: anything this crate knows how to feed
329/// into a WIT `stream<u8>`.
330///
331/// Operation methods take `impl Into<DataSource<'_>>`, so byte slices, owned
332/// buffers, and streams received from other components all work directly:
333///
334/// - `&[u8]`, `&[u8; N]`, `Vec<u8>`, `&Vec<u8>`, [`Cow<'a, [u8]>`](Cow) —
335///   buffered sources. Owned data is moved and written whole, never copied;
336///   borrowed data is fed chunk by chunk through one reusable buffer, so a
337///   large input is never duplicated whole (the ABI's per-chunk copy into
338///   an owned buffer is unavoidable).
339/// - [`StreamReader<u8>`] — passed through to the operation as-is, without
340///   buffering.
341/// - `DataSource::from_buf` (feature `bytes`) — fed chunk by chunk.
342/// - `DataSource::from_reader` (feature `futures-io`) — pumped
343///   incrementally; read failures surface as [`Error::Read`].
344///
345/// # Warning: truncating producers
346///
347/// Dropping a stream's writer is its only end-of-input signal and carries no
348/// verdict, so a producer that fails midway is indistinguishable *on the
349/// wire* from one that finished — the operation correctly computes over the
350/// delivered prefix. Buffer-backed sources own their whole input and are
351/// immune. For a [`StreamReader<u8>`] fed by another component, convey
352/// completeness in-band (e.g. length framing) or discard the result on
353/// producer failure. A `DataSource::from_reader` source is handled for
354/// you: its failure is observed locally and reported as [`Error::Read`]
355/// instead of the operation's result.
356pub struct DataSource<'a>(Inner<'a>);
357
358enum Inner<'a> {
359    Bytes(Cow<'a, [u8]>),
360    Stream(StreamReader<u8>),
361    #[cfg(feature = "bytes")]
362    Buf(Box<dyn ::bytes::Buf + 'a>),
363    #[cfg(feature = "futures-io")]
364    Reader(std::pin::Pin<Box<dyn futures_io::AsyncRead + 'a>>),
365}
366
367impl<'a> From<&'a [u8]> for DataSource<'a> {
368    fn from(data: &'a [u8]) -> Self {
369        Self(Inner::Bytes(Cow::Borrowed(data)))
370    }
371}
372
373impl<'a, const N: usize> From<&'a [u8; N]> for DataSource<'a> {
374    fn from(data: &'a [u8; N]) -> Self {
375        Self(Inner::Bytes(Cow::Borrowed(data)))
376    }
377}
378
379impl From<Vec<u8>> for DataSource<'_> {
380    fn from(data: Vec<u8>) -> Self {
381        Self(Inner::Bytes(Cow::Owned(data)))
382    }
383}
384
385impl<'a> From<&'a Vec<u8>> for DataSource<'a> {
386    fn from(data: &'a Vec<u8>) -> Self {
387        Self(Inner::Bytes(Cow::Borrowed(data)))
388    }
389}
390
391impl<'a> From<Cow<'a, [u8]>> for DataSource<'a> {
392    fn from(data: Cow<'a, [u8]>) -> Self {
393        Self(Inner::Bytes(data))
394    }
395}
396
397impl From<StreamReader<u8>> for DataSource<'_> {
398    fn from(stream: StreamReader<u8>) -> Self {
399        Self(Inner::Stream(stream))
400    }
401}
402
403impl<'a> DataSource<'a> {
404    /// A source that feeds the operation from `buf`, chunk by chunk.
405    ///
406    /// `Buf` is infallible, so this source cannot produce [`Error::Read`].
407    #[cfg(feature = "bytes")]
408    pub fn from_buf(buf: impl ::bytes::Buf + 'a) -> Self {
409        Self(Inner::Buf(Box::new(buf)))
410    }
411
412    /// A source that pumps the operation's input from `reader` until
413    /// end-of-file.
414    ///
415    /// A read failure aborts the feed and the operation reports
416    /// [`Error::Read`] — never the result computed over the truncated
417    /// prefix.
418    #[cfg(feature = "futures-io")]
419    pub fn from_reader(reader: impl futures_io::AsyncRead + 'a) -> Self {
420        Self(Inner::Reader(Box::pin(reader)))
421    }
422}
423
424// --- operation plumbing ---------------------------------------------------------
425
426/// The chunk size the incremental feeders copy through their reusable
427/// scratch buffer, bounding a feed's extra memory to one chunk.
428const FEED_CHUNK: usize = 8192;
429
430/// A feeder's outcome, distinct from its *failure* ([`Error::Read`]): a
431/// rejected write is not itself an error — the closure rule permits a
432/// failing operation to stop reading — so its meaning depends on the
433/// operation's result.
434#[must_use]
435enum Feed {
436    /// Every byte was written.
437    Complete,
438    /// The operation stopped accepting input partway.
439    Rejected,
440}
441
442impl Feed {
443    /// The success-path requirement: a completed operation promises it
444    /// consumed the whole input, so rejection under success is the defect
445    /// [`Error::ShortWrite`] names.
446    fn require_complete(self) -> Result<(), Error> {
447        match self {
448            Feed::Complete => Ok(()),
449            Feed::Rejected => Err(Error::ShortWrite),
450        }
451    }
452}
453
454impl Inner<'_> {
455    /// Feed this source into `tx`, then drop the writer to end the stream.
456    /// The error is always [`Error::Read`] — the only way a feed *fails*;
457    /// the operation rejecting input is an outcome, not an error.
458    async fn feed(self, mut tx: StreamWriter<u8>) -> Result<Feed, Error> {
459        match self {
460            // Pass-through sources never reach the feeder.
461            Inner::Stream(_) => unreachable!("stream sources are passed through"),
462            Inner::Bytes(Cow::Owned(data)) => {
463                let leftover = tx.write_all(data).await;
464                if leftover.is_empty() {
465                    Ok(Feed::Complete)
466                } else {
467                    Ok(Feed::Rejected)
468                }
469            }
470            // A borrowed buffer is never duplicated whole: it is fed in
471            // chunks through one reusable allocation (`write_all` returns
472            // its argument's allocation, emptied on success), so the feed
473            // costs one chunk of extra memory and the ABI's unavoidable
474            // per-chunk copy.
475            Inner::Bytes(Cow::Borrowed(data)) => {
476                let mut scratch = Vec::new();
477                for chunk in data.chunks(FEED_CHUNK) {
478                    scratch.extend_from_slice(chunk);
479                    scratch = tx.write_all(scratch).await;
480                    if !scratch.is_empty() {
481                        return Ok(Feed::Rejected);
482                    }
483                }
484                Ok(Feed::Complete)
485            }
486            #[cfg(feature = "bytes")]
487            Inner::Buf(mut buf) => {
488                use ::bytes::Buf as _;
489                // As for borrowed bytes: one reusable scratch buffer, one
490                // copy per source-native chunk.
491                let mut scratch = Vec::new();
492                while buf.has_remaining() {
493                    let chunk = buf.chunk();
494                    let n = chunk.len();
495                    scratch.extend_from_slice(chunk);
496                    buf.advance(n);
497                    scratch = tx.write_all(scratch).await;
498                    if !scratch.is_empty() {
499                        return Ok(Feed::Rejected);
500                    }
501                }
502                Ok(Feed::Complete)
503            }
504            #[cfg(feature = "futures-io")]
505            Inner::Reader(mut reader) => {
506                // As for borrowed bytes: one reusable scratch buffer, one
507                // copy per chunk (out of the read buffer `poll_read`
508                // requires).
509                let mut chunk = [0u8; FEED_CHUNK];
510                let mut scratch = Vec::new();
511                loop {
512                    let n = std::future::poll_fn(|cx| reader.as_mut().poll_read(cx, &mut chunk))
513                        .await
514                        .map_err(Error::Read)?;
515                    if n == 0 {
516                        return Ok(Feed::Complete);
517                    }
518                    scratch.extend_from_slice(&chunk[..n]);
519                    scratch = tx.write_all(scratch).await;
520                    if !scratch.is_empty() {
521                        return Ok(Feed::Rejected);
522                    }
523                }
524            }
525        }
526    }
527}
528
529/// Run the operation built by `op` over `source`: pass a stream source
530/// through directly, or mint a stream pair and feed the source concurrently
531/// with the operation (per the closure rule, the feed settles no later
532/// than the operation). The joined outcome resolves one precedence rule
533/// per statement: a source failure outranks everything (the operation only
534/// saw a truncated input); then the operation's result is authoritative;
535/// and a completed operation must have consumed the whole input.
536async fn run_sourced<T, F>(
537    source: DataSource<'_>,
538    op: impl FnOnce(StreamReader<u8>) -> F,
539) -> Result<T, Error>
540where
541    F: std::future::Future<Output = Result<T, bindings::types::Error>>,
542{
543    match source.0 {
544        Inner::Stream(rx) => op(rx).await.map_err(Error::from),
545        inner => {
546            let (tx, rx) = wit_stream::new();
547            let (result, fed) = futures::join!(op(rx), inner.feed(tx));
548            let fed = fed?;
549            let value = result?;
550            fed.require_complete()?;
551            Ok(value)
552        }
553    }
554}
555
556/// A pending `seal`, returned by [`Aead::seal`] and
557/// [`CipherKey::encrypt`].
558///
559/// Nothing runs until this is polled: the operation starts on the first
560/// `await`, so a `Seal` that is dropped unused never calls the
561/// implementation.
562///
563/// Awaiting it yields the whole sealed message. It is a [`Future`] rather
564/// than an `async fn`'s anonymous one so that it drops straight into
565/// [`futures::join!`], which is the shape the package's making-progress rule
566/// asks callers for: several operations in flight, all of them making
567/// progress.
568///
569/// `seal` is the one operation in the package whose result may arrive before
570/// its input is consumed — the WIT permits producing the sealed message
571/// incrementally — so the collect runs *concurrently* with the feed. Awaiting
572/// the operation first and reading the stream afterwards would deadlock
573/// against a provider that does so.
574#[must_use = "a Seal does nothing until it is awaited"]
575pub struct Seal<'a> {
576    state: SealState<'a>,
577}
578
579type LocalBoxFuture<'a, T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + 'a>>;
580
581/// Starts the operation over the readable end of a freshly minted stream.
582type StartSeal<'a> = Box<
583    dyn FnOnce(
584            StreamReader<u8>,
585        ) -> LocalBoxFuture<'a, Result<StreamReader<u8>, bindings::types::Error>>
586        + 'a,
587>;
588
589enum SealState<'a> {
590    Ready(DataSource<'a>, StartSeal<'a>),
591    Running(LocalBoxFuture<'a, Result<Vec<u8>, Error>>),
592    Done,
593}
594
595impl<'a> Seal<'a> {
596    fn new(source: DataSource<'a>, start: StartSeal<'a>) -> Self {
597        Self {
598            state: SealState::Ready(source, start),
599        }
600    }
601}
602
603impl std::future::Future for Seal<'_> {
604    type Output = Result<Vec<u8>, Error>;
605
606    fn poll(
607        self: std::pin::Pin<&mut Self>,
608        cx: &mut std::task::Context<'_>,
609    ) -> std::task::Poll<Self::Output> {
610        // `Seal` is `Unpin`: every field is a `Box` or a `Pin<Box<_>>`.
611        let this = self.get_mut();
612        if let SealState::Ready(..) = this.state {
613            let SealState::Ready(source, start) =
614                std::mem::replace(&mut this.state, SealState::Done)
615            else {
616                unreachable!("just matched Ready")
617            };
618            this.state = SealState::Running(seal_and_collect(source, start));
619        }
620        match &mut this.state {
621            SealState::Running(running) => running.as_mut().poll(cx),
622            SealState::Done => panic!("Seal polled after completion"),
623            SealState::Ready(..) => unreachable!("started above"),
624        }
625    }
626}
627
628/// Feed the source and collect the sealed message concurrently.
629fn seal_and_collect<'a>(
630    source: DataSource<'a>,
631    start: StartSeal<'a>,
632) -> LocalBoxFuture<'a, Result<Vec<u8>, Error>> {
633    Box::pin(async move {
634        match source.0 {
635            // A caller-supplied stream is fed by whoever owns its writer, so
636            // there is nothing to run concurrently here.
637            Inner::Stream(rx) => {
638                let sealed = start(rx).await.map_err(Error::from)?;
639                Ok(sealed.collect().await)
640            }
641            inner => {
642                let (tx, rx) = wit_stream::new();
643                let sealed = async {
644                    let stream = start(rx).await.map_err(Error::from)?;
645                    Ok::<_, Error>(stream.collect().await)
646                };
647                let (result, fed) = futures::join!(sealed, inner.feed(tx));
648                let fed = fed?;
649                let value = result?;
650                fed.require_complete()?;
651                Ok(value)
652            }
653        }
654    })
655}
656
657// --- key options ----------------------------------------------------------------
658
659/// Mint-time policy for a [`Mac`] key: the plain-data counterpart of the
660/// WIT `mac.mac-key-options` resource, which the minting functions
661/// construct from it per call.
662///
663/// Follows the package-wide options contract: the default grants nothing,
664/// every field is opt-in, and a mint with no usage enabled fails
665/// [`Error::NotPermitted`].
666#[derive(Clone, Copy, Debug, Default)]
667pub struct MacKeyOptions {
668    /// Whether the minted key may `sign`.
669    pub sign: bool,
670    /// Whether the minted key may `verify`.
671    pub verify: bool,
672    /// Whether the minted key's material may be exported.
673    pub extractable: bool,
674}
675
676impl MacKeyOptions {
677    /// The WIT options resource carrying this policy.
678    pub(crate) fn lower(self) -> bindings::mac::MacKeyOptions {
679        let options = bindings::mac::MacKeyOptions::new();
680        options.can_sign(self.sign);
681        options.can_verify(self.verify);
682        options.extractable(self.extractable);
683        options
684    }
685}
686
687/// Mint-time policy for an [`Aead`] key. See [`MacKeyOptions`] for the
688/// options contract.
689#[derive(Clone, Copy, Debug, Default)]
690pub struct AeadKeyOptions {
691    /// Whether the minted key may `seal`.
692    pub seal: bool,
693    /// Whether the minted key may `open`.
694    pub open: bool,
695    /// Whether the minted key may `wrap` key material.
696    pub wrap: bool,
697    /// Whether the minted key may `unwrap` key material.
698    pub unwrap: bool,
699    /// Whether the minted key's material may be exported.
700    pub extractable: bool,
701}
702
703impl AeadKeyOptions {
704    /// The WIT options resource carrying this policy.
705    pub(crate) fn lower(self) -> bindings::aead::AeadKeyOptions {
706        let options = bindings::aead::AeadKeyOptions::new();
707        options.can_seal(self.seal);
708        options.can_open(self.open);
709        options.can_wrap(self.wrap);
710        options.can_unwrap(self.unwrap);
711        options.extractable(self.extractable);
712        options
713    }
714}
715
716/// Mint-time policy for a [`CipherKey`] key. See [`MacKeyOptions`] for the
717/// options contract.
718#[derive(Clone, Copy, Debug, Default)]
719pub struct CipherKeyOptions {
720    /// Whether the minted key may `encrypt`.
721    pub encrypt: bool,
722    /// Whether the minted key may `decrypt`.
723    pub decrypt: bool,
724    /// Whether the minted key may `wrap` key material.
725    pub wrap: bool,
726    /// Whether the minted key may `unwrap` key material.
727    pub unwrap: bool,
728    /// Whether the minted key's material may be exported.
729    pub extractable: bool,
730}
731
732impl CipherKeyOptions {
733    /// The WIT options resource carrying this policy.
734    pub(crate) fn lower(self) -> bindings::cipher::CipherKeyOptions {
735        let options = bindings::cipher::CipherKeyOptions::new();
736        options.can_encrypt(self.encrypt);
737        options.can_decrypt(self.decrypt);
738        options.can_wrap(self.wrap);
739        options.can_unwrap(self.unwrap);
740        options.extractable(self.extractable);
741        options
742    }
743}
744
745/// Mint-time policy for a [`KwKey`]. See [`MacKeyOptions`] for the options
746/// contract.
747#[derive(Clone, Copy, Debug, Default)]
748pub struct KwKeyOptions {
749    /// Whether the minted key may `wrap` key material.
750    pub wrap: bool,
751    /// Whether the minted key may `unwrap` key material.
752    pub unwrap: bool,
753    /// Whether the minted key's material may be exported.
754    pub extractable: bool,
755}
756
757impl KwKeyOptions {
758    /// The WIT options resource carrying this policy.
759    pub(crate) fn lower(self) -> bindings::key_wrap::KwKeyOptions {
760        let options = bindings::key_wrap::KwKeyOptions::new();
761        options.can_wrap(self.wrap);
762        options.can_unwrap(self.unwrap);
763        options.extractable(self.extractable);
764        options
765    }
766}
767
768/// Mint-time policy for a [`SigningKey`]. See [`MacKeyOptions`] for the
769/// options contract; `sign` is the sole usage, so it must be enabled for a
770/// mint to succeed.
771#[derive(Clone, Copy, Debug, Default)]
772pub struct SigningKeyOptions {
773    /// Whether the minted key may `sign`.
774    pub sign: bool,
775    /// Whether the minted key's material may be exported (by future
776    /// format-specific exports; there is no export operation today).
777    pub extractable: bool,
778}
779
780impl SigningKeyOptions {
781    /// The WIT options resource carrying this policy.
782    pub(crate) fn lower(self) -> bindings::signature::SigningKeyOptions {
783        let options = bindings::signature::SigningKeyOptions::new();
784        options.can_sign(self.sign);
785        options.extractable(self.extractable);
786        options
787    }
788}
789
790/// Mint-time policy for a derivation base secret ([`Ikm`] or [`Password`]).
791/// See [`MacKeyOptions`] for the options contract. The grants are copied
792/// onto every [`DeriveInput`] built on the secret; parameterization
793/// neither grants nor revokes.
794#[derive(Clone, Copy, Debug, Default)]
795pub struct DeriveOptions {
796    /// Whether inputs built on this secret may yield raw bits through
797    /// [`DeriveInput::derive_bits`] — and, because an exportable key is
798    /// bits disclosure by other means, whether they may mint
799    /// *extractable* keys.
800    pub derive_bits: bool,
801    /// Whether inputs built on this secret may mint keys through the
802    /// target interfaces' `derive_key` (e.g. [`hmac_sha2::derive_key`]).
803    pub derive_key: bool,
804}
805
806impl DeriveOptions {
807    /// The WIT options resource carrying this policy.
808    pub(crate) fn lower(self) -> bindings::derivation::DeriveOptions {
809        let options = bindings::derivation::DeriveOptions::new();
810        options.can_derive_bits(self.derive_bits);
811        options.can_derive_key(self.derive_key);
812        options
813    }
814}
815
816/// Mint-time policy for an [`AgreementSecretKey`]. See [`MacKeyOptions`]
817/// for the options contract; the derive grants are copied onto every
818/// [`DeriveInput`] the key [`agree`](AgreementSecretKey::agree)s
819/// (WebCrypto's model: derive usages live on the secret key).
820#[derive(Clone, Copy, Debug, Default)]
821pub struct AgreementKeyOptions {
822    /// Whether inputs agreed by the key may yield raw bits. See
823    /// [`DeriveOptions::derive_bits`].
824    pub derive_bits: bool,
825    /// Whether inputs agreed by the key may mint keys. See
826    /// [`DeriveOptions::derive_key`].
827    pub derive_key: bool,
828    /// Whether the secret key's material may be exported.
829    pub extractable: bool,
830}
831
832impl AgreementKeyOptions {
833    /// The WIT options resource carrying this policy.
834    pub(crate) fn lower(self) -> bindings::key_agreement::AgreementKeyOptions {
835        let options = bindings::key_agreement::AgreementKeyOptions::new();
836        options.can_derive_bits(self.derive_bits);
837        options.can_derive_key(self.derive_key);
838        options.extractable(self.extractable);
839        options
840    }
841}
842
843/// Mint-time policy for a [`DecryptionKey`]. See [`MacKeyOptions`] for the
844/// options contract. The two grants separate disclosure from minting:
845/// `decrypt` returns plaintext to the caller, while `unwrap` mints keys
846/// whose material the caller never sees, so a key granted only `unwrap`
847/// cannot leak what it transports.
848#[derive(Clone, Copy, Debug, Default)]
849pub struct DecryptionKeyOptions {
850    /// Whether the minted key may [`decrypt`](DecryptionKey::decrypt).
851    pub decrypt: bool,
852    /// Whether the minted key may [`unwrap`](DecryptionKey::unwrap).
853    pub unwrap: bool,
854    /// Whether the minted key's material may be exported.
855    pub extractable: bool,
856}
857
858impl DecryptionKeyOptions {
859    /// The WIT options resource carrying this policy.
860    #[cfg_attr(not(feature = "rsa-oaep-decrypt"), allow(dead_code))]
861    pub(crate) fn lower(self) -> bindings::public_encryption::DecryptionKeyOptions {
862        let options = bindings::public_encryption::DecryptionKeyOptions::new();
863        options.can_decrypt(self.decrypt);
864        options.can_unwrap(self.unwrap);
865        options.extractable(self.extractable);
866        options
867    }
868}
869
870// --- newtypes ------------------------------------------------------------------
871
872/// Generate the shared newtype plumbing: constructors, raw accessors, and
873/// `From` in both directions.
874macro_rules! newtype_common {
875    ($name:ident, $raw:ty, $doc_res:literal) => {
876        impl $name {
877            #[doc = concat!("Wrap a raw `", $doc_res, "` resource.")]
878            pub fn from_raw(raw: $raw) -> Self {
879                Self(raw)
880            }
881
882            #[doc = concat!("Borrow the raw `", $doc_res, "` resource.")]
883            pub fn as_raw(&self) -> &$raw {
884                &self.0
885            }
886
887            #[doc = concat!("Unwrap into the raw `", $doc_res, "` resource.")]
888            pub fn into_raw(self) -> $raw {
889                self.0
890            }
891        }
892
893        impl From<$raw> for $name {
894            fn from(raw: $raw) -> Self {
895                Self(raw)
896            }
897        }
898
899        impl std::fmt::Debug for $name {
900            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
901                f.debug_tuple(stringify!($name)).field(&self.0).finish()
902            }
903        }
904    };
905}
906
907/// A `mac.mac-key`: a message-authentication-code key, bound to one
908/// algorithm at creation.
909pub struct Mac(bindings::mac::MacKey);
910newtype_common!(Mac, bindings::mac::MacKey, "mac-key");
911
912impl Mac {
913    /// Compute the authentication tag over `data`.
914    ///
915    /// Fails only for operational reasons ([`Error::Other`], or
916    /// [`Error::Read`] for a failing `DataSource::from_reader` source) —
917    /// never for misuse, which is unrepresentable.
918    pub async fn sign(&self, data: impl Into<DataSource<'_>>) -> Result<Vec<u8>, Error> {
919        run_sourced(data.into(), |rx| self.0.sign(rx)).await
920    }
921
922    /// Verify `tag` over `data`, in constant time.
923    ///
924    /// Fails closed with [`Error::AuthenticationFailed`] if the tag does not
925    /// verify — deliberately a `Result` rather than a `bool`: an ignored
926    /// boolean fails open, a dropped `Result` does not.
927    pub async fn verify(
928        &self,
929        data: impl Into<DataSource<'_>>,
930        tag: impl Into<Cow<'_, [u8]>>,
931    ) -> Result<(), Error> {
932        let tag = tag.into().into_owned();
933        run_sourced(data.into(), |rx| self.0.verify(rx, tag)).await
934    }
935
936    /// The name of the key's algorithm family, e.g. `"HMAC"` — WebCrypto's
937    /// `KeyAlgorithm.name`, spelled as the [W3C Web Cryptography API
938    /// algorithm registry](https://www.w3.org/TR/WebCryptoAPI/#algorithm-overview)
939    /// spells it.
940    pub fn algorithm_name(&self) -> String {
941        self.0.algorithm_name()
942    }
943
944    /// The registry name of the digest the algorithm is parameterized over,
945    /// e.g. `"SHA-256"` for HMAC-SHA-256 (WebCrypto's
946    /// `HmacKeyAlgorithm.hash`, spelled per the same registry as
947    /// [`algorithm_name`](Self::algorithm_name)). `None` for MAC algorithms
948    /// not built on a digest.
949    pub fn algorithm_hash(&self) -> Option<String> {
950        self.0.algorithm_hash()
951    }
952
953    /// The key length in bits (WebCrypto's `HmacKeyAlgorithm.length`: the
954    /// length of the key material).
955    pub fn algorithm_length(&self) -> u32 {
956        self.0.algorithm_length()
957    }
958
959    /// Whether [`export_key_raw`](Self::export_key_raw) may return the key
960    /// material.
961    ///
962    /// Asking is not the same as exporting: interrogating extractability
963    /// through [`export_key_raw`](Self::export_key_raw) alone would hand you the
964    /// material whenever the answer is yes.
965    pub fn extractable(&self) -> bool {
966        self.0.extractable()
967    }
968
969    /// Whether the key permits [`sign`](Self::sign) — the usage recorded
970    /// at mint. A refused operation fails [`Error::NotPermitted`].
971    pub fn can_sign(&self) -> bool {
972        self.0.can_sign()
973    }
974
975    /// Whether the key permits [`verify`](Self::verify). See
976    /// [`can_sign`](Self::can_sign).
977    pub fn can_verify(&self) -> bool {
978        self.0.can_verify()
979    }
980
981    /// The raw key material; fails with [`Error::NotExtractable`] unless the
982    /// key was minted extractable. Extractability is an API property, not a
983    /// physical one: the guarantee is that components holding only the
984    /// handle cannot obtain the material through this API.
985    pub async fn export_key_raw(&self) -> Result<Vec<u8>, Error> {
986        self.0.export_key_raw().await.map_err(Error::from)
987    }
988
989    /// The key as an RFC 7517 `oct` JSON Web Key (JSON text), behind the
990    /// same extractability gate as [`export_key_raw`](Self::export_key_raw).
991    pub async fn export_key_jwk(&self) -> Result<String, Error> {
992        self.0.export_key_jwk().await.map_err(Error::from)
993    }
994
995    /// This key's raw material as a [`WrapInput`], behind the same
996    /// extractability gate as [`export_key_raw`](Self::export_key_raw).
997    pub async fn to_wrap_input_raw(&self) -> Result<WrapInput, Error> {
998        self.0
999            .to_wrap_input_raw()
1000            .await
1001            .map(WrapInput::from_raw)
1002            .map_err(Error::from)
1003    }
1004
1005    /// The JWK serialization as a [`WrapInput`], behind the same gate.
1006    pub async fn to_wrap_input_jwk(&self) -> Result<WrapInput, Error> {
1007        self.0
1008            .to_wrap_input_jwk()
1009            .await
1010            .map(WrapInput::from_raw)
1011            .map_err(Error::from)
1012    }
1013}
1014
1015/// An `aead.aead-key`: caller-nonce authenticated encryption with
1016/// associated data.
1017///
1018/// Nonce reuse under one key is catastrophic, and this type's
1019/// [`seal`](Self::seal) leaves nonce uniqueness entirely to you: use a
1020/// deterministic per-key uniqueness scheme (such as a counter).
1021pub struct Aead(bindings::aead::AeadKey);
1022newtype_common!(Aead, bindings::aead::AeadKey, "aead-key");
1023
1024impl Aead {
1025    /// Encrypt and authenticate `plaintext` under `nonce` and `aad`,
1026    /// yielding the ciphertext followed by the authentication tag.
1027    ///
1028    /// Returns a [`Seal`], which starts the operation when awaited.
1029    ///
1030    /// **The caller is responsible for nonce uniqueness per key.** Reusing a
1031    /// nonce under one key defeats the algorithm's confidentiality and
1032    /// authenticity guarantees.
1033    pub fn seal<'a>(
1034        &'a self,
1035        nonce: impl Into<Cow<'a, [u8]>>,
1036        aad: impl Into<Cow<'a, [u8]>>,
1037        plaintext: impl Into<DataSource<'a>>,
1038    ) -> Seal<'a> {
1039        let (nonce, aad) = (nonce.into().into_owned(), aad.into().into_owned());
1040        Seal::new(
1041            plaintext.into(),
1042            Box::new(move |rx| Box::pin(self.0.seal(nonce, aad, None, rx))),
1043        )
1044    }
1045
1046    /// [`seal`](Self::seal) with an explicit tag size in bytes, for
1047    /// algorithms whose tag size is a per-call parameter (AES-GCM's set is
1048    /// 4, 8, 12, 13, 14, 15, or 16; other algorithms fix 16). Short tags
1049    /// weaken the forgery bound; prefer [`seal`](Self::seal), which uses
1050    /// the algorithm default ([`tag_size`](Self::tag_size)).
1051    pub fn seal_with_tag_size<'a>(
1052        &'a self,
1053        nonce: impl Into<Cow<'a, [u8]>>,
1054        aad: impl Into<Cow<'a, [u8]>>,
1055        tag_size: u8,
1056        plaintext: impl Into<DataSource<'a>>,
1057    ) -> Seal<'a> {
1058        let (nonce, aad) = (nonce.into().into_owned(), aad.into().into_owned());
1059        Seal::new(
1060            plaintext.into(),
1061            Box::new(move |rx| Box::pin(self.0.seal(nonce, aad, Some(tag_size), rx))),
1062        )
1063    }
1064
1065    /// Decrypt and verify `ciphertext` (ciphertext followed by tag, as
1066    /// produced by [`seal`](Self::seal)) under `nonce` and `aad`.
1067    ///
1068    /// The stream is handed back only after the whole input is consumed and
1069    /// the tag verified: `Ok` *is* the authentication statement, and
1070    /// unverified plaintext is never observable. Fails closed with
1071    /// [`Error::AuthenticationFailed`] if verification fails.
1072    pub async fn open(
1073        &self,
1074        nonce: impl Into<Cow<'_, [u8]>>,
1075        aad: impl Into<Cow<'_, [u8]>>,
1076        ciphertext: impl Into<DataSource<'_>>,
1077    ) -> Result<StreamReader<u8>, Error> {
1078        let (nonce, aad) = (nonce.into().into_owned(), aad.into().into_owned());
1079        run_sourced(ciphertext.into(), |rx| self.0.open(nonce, aad, None, rx)).await
1080    }
1081
1082    /// [`open`](Self::open) with an explicit tag size in bytes (the size
1083    /// the message was sealed with — see
1084    /// [`seal_with_tag_size`](Self::seal_with_tag_size)).
1085    pub async fn open_with_tag_size(
1086        &self,
1087        nonce: impl Into<Cow<'_, [u8]>>,
1088        aad: impl Into<Cow<'_, [u8]>>,
1089        tag_size: u8,
1090        ciphertext: impl Into<DataSource<'_>>,
1091    ) -> Result<StreamReader<u8>, Error> {
1092        let (nonce, aad) = (nonce.into().into_owned(), aad.into().into_owned());
1093        run_sourced(ciphertext.into(), |rx| {
1094            self.0.open(nonce, aad, Some(tag_size), rx)
1095        })
1096        .await
1097    }
1098
1099    /// The name of the key's algorithm family, e.g. `"AES-GCM"` —
1100    /// WebCrypto's `KeyAlgorithm.name`, spelled as the [W3C Web Cryptography
1101    /// API algorithm registry](https://www.w3.org/TR/WebCryptoAPI/#algorithm-overview)
1102    /// spells it.
1103    pub fn algorithm_name(&self) -> String {
1104        self.0.algorithm_name()
1105    }
1106
1107    /// The key length in bits, e.g. `256` for AES-256-GCM (WebCrypto's
1108    /// `AesKeyAlgorithm.length`).
1109    pub fn algorithm_length(&self) -> u32 {
1110        self.0.algorithm_length()
1111    }
1112
1113    /// The algorithm's standard nonce size in bytes, e.g. `12` for AES-GCM
1114    /// — always accepted by [`seal`](Self::seal)/[`open`](Self::open).
1115    /// Whether other lengths are accepted is the algorithm's contract
1116    /// (AES-GCM accepts 12 to 128 bytes inclusive).
1117    pub fn nonce_size(&self) -> u32 {
1118        self.0.nonce_size()
1119    }
1120
1121    /// The size in bytes of the tag trailing the ciphertext, e.g. `16` —
1122    /// for framing arithmetic (sealed length = plaintext length +
1123    /// `tag_size`).
1124    pub fn tag_size(&self) -> u32 {
1125        self.0.tag_size()
1126    }
1127
1128    /// Whether [`export_key_raw`](Self::export_key_raw) may return the key material
1129    /// (see [`Mac::extractable`]).
1130    pub fn extractable(&self) -> bool {
1131        self.0.extractable()
1132    }
1133
1134    /// Whether the key permits [`seal`](Self::seal) — the usage recorded
1135    /// at mint. A refused operation fails [`Error::NotPermitted`].
1136    pub fn can_seal(&self) -> bool {
1137        self.0.can_seal()
1138    }
1139
1140    /// Whether the key permits [`open`](Self::open). See
1141    /// [`can_seal`](Self::can_seal).
1142    pub fn can_open(&self) -> bool {
1143        self.0.can_open()
1144    }
1145
1146    /// Whether the key permits [`wrap`](Self::wrap).
1147    pub fn can_wrap(&self) -> bool {
1148        self.0.can_wrap()
1149    }
1150
1151    /// Whether the key permits [`unwrap`](Self::unwrap). See
1152    /// [`can_wrap`](Self::can_wrap).
1153    pub fn can_unwrap(&self) -> bool {
1154        self.0.can_unwrap()
1155    }
1156
1157    /// Encrypt and authenticate serialized key material under `nonce`
1158    /// with `aad`, exactly as `seal` encrypts a message (the WIT
1159    /// `aead-key.wrap` contract). Consumes the [`WrapInput`].
1160    pub async fn wrap(
1161        &self,
1162        nonce: impl Into<Vec<u8>>,
1163        aad: impl Into<Vec<u8>>,
1164        tag_size: Option<u8>,
1165        input: WrapInput,
1166    ) -> Result<Vec<u8>, Error> {
1167        self.0
1168            .wrap(nonce.into(), aad.into(), tag_size, input.into_raw())
1169            .await
1170            .map_err(Error::from)
1171    }
1172
1173    /// Decrypt and verify wrapped key material into an [`UnwrapInput`]
1174    /// for a typed unwrap mint (the WIT `aead-key.unwrap` contract).
1175    pub async fn unwrap(
1176        &self,
1177        nonce: impl Into<Vec<u8>>,
1178        aad: impl Into<Vec<u8>>,
1179        tag_size: Option<u8>,
1180        wrapped: impl Into<Vec<u8>>,
1181    ) -> Result<UnwrapInput, Error> {
1182        self.0
1183            .unwrap(nonce.into(), aad.into(), tag_size, wrapped.into())
1184            .await
1185            .map(UnwrapInput::from_raw)
1186            .map_err(Error::from)
1187    }
1188
1189    /// This key's raw material as a [`WrapInput`], behind the same
1190    /// extractability gate as [`export_key_raw`](Self::export_key_raw).
1191    pub async fn to_wrap_input_raw(&self) -> Result<WrapInput, Error> {
1192        self.0
1193            .to_wrap_input_raw()
1194            .await
1195            .map(WrapInput::from_raw)
1196            .map_err(Error::from)
1197    }
1198
1199    /// The JWK serialization as a [`WrapInput`], behind the same gate.
1200    pub async fn to_wrap_input_jwk(&self) -> Result<WrapInput, Error> {
1201        self.0
1202            .to_wrap_input_jwk()
1203            .await
1204            .map(WrapInput::from_raw)
1205            .map_err(Error::from)
1206    }
1207
1208    /// The raw key material; fails with [`Error::NotExtractable`] unless the
1209    /// key was minted extractable (an API property, not a physical one —
1210    /// see [`Mac::export_key_raw`]).
1211    pub async fn export_key_raw(&self) -> Result<Vec<u8>, Error> {
1212        self.0.export_key_raw().await.map_err(Error::from)
1213    }
1214
1215    /// The key as an RFC 7517 `oct` JSON Web Key (JSON text), behind the
1216    /// same extractability gate as [`export_key_raw`](Self::export_key_raw).
1217    /// Algorithms with no registered JWK form fail [`Error::Unsupported`].
1218    pub async fn export_key_jwk(&self) -> Result<String, Error> {
1219        self.0.export_key_jwk().await.map_err(Error::from)
1220    }
1221}
1222
1223/// A `digest.digest`: a reusable, algorithm-bound hash.
1224///
1225/// A digest authenticates nothing by itself: to check untrusted data
1226/// against a known digest, compare [`compute`](Self::compute)'s result
1227/// with a constant-time byte comparison; when authenticity is needed, use
1228/// a [`Mac`].
1229pub struct Digest(bindings::digest::Digest);
1230newtype_common!(Digest, bindings::digest::Digest, "digest");
1231
1232impl Digest {
1233    /// Digest `data`. The resource is reusable; the result is
1234    /// chunking-invariant.
1235    pub async fn compute(&self, data: impl Into<DataSource<'_>>) -> Result<Vec<u8>, Error> {
1236        run_sourced(data.into(), |rx| self.0.compute(rx)).await
1237    }
1238
1239    /// The name of the algorithm this resource is bound to, e.g.
1240    /// `"SHA-256"` — spelled as the [W3C Web Cryptography API algorithm
1241    /// registry](https://www.w3.org/TR/WebCryptoAPI/#algorithm-overview)
1242    /// (and `crypto.subtle.digest`) spells it.
1243    pub fn algorithm_name(&self) -> String {
1244        self.0.algorithm_name()
1245    }
1246}
1247
1248/// A `signature.verifying-key`: public-key signature verification.
1249/// Secret-free — a component holding only this key provably cannot sign.
1250pub struct VerifyingKey(bindings::signature::VerifyingKey);
1251newtype_common!(
1252    VerifyingKey,
1253    bindings::signature::VerifyingKey,
1254    "verifying-key"
1255);
1256
1257impl VerifyingKey {
1258    /// Verify `sig` over `data`.
1259    ///
1260    /// Fails closed with [`Error::AuthenticationFailed`] if the signature
1261    /// does not verify — deliberately a `Result` rather than a `bool`: an
1262    /// ignored boolean fails open, a dropped `Result` does not. The precise
1263    /// verification criterion (which degenerate keys and signatures must be
1264    /// rejected) is defined by the key's minting interface, exactly like
1265    /// the wire format.
1266    pub async fn verify(
1267        &self,
1268        data: impl Into<DataSource<'_>>,
1269        sig: impl Into<Cow<'_, [u8]>>,
1270    ) -> Result<(), Error> {
1271        let sig = sig.into().into_owned();
1272        run_sourced(data.into(), |rx| self.0.verify(rx, sig)).await
1273    }
1274
1275    /// The name of the key's algorithm family, e.g. `"Ed25519"` or
1276    /// `"ECDSA"` — WebCrypto's `KeyAlgorithm.name`, spelled as the [W3C Web
1277    /// Cryptography API algorithm
1278    /// registry](https://www.w3.org/TR/WebCryptoAPI/#algorithm-overview)
1279    /// spells it.
1280    pub fn algorithm_name(&self) -> String {
1281        self.0.algorithm_name()
1282    }
1283
1284    /// The registry name of the curve for curve-parameterized algorithms,
1285    /// e.g. `"P-256"` (WebCrypto's `EcKeyAlgorithm.namedCurve`). `None` for
1286    /// Ed25519, whose curve is implied by the name.
1287    pub fn algorithm_curve(&self) -> Option<String> {
1288        self.0.algorithm_curve()
1289    }
1290
1291    /// The registry name of the digest bound at mint, e.g. `"SHA-256"`.
1292    /// `None` for Ed25519: RFC 8032 fixes SHA-512 internally, so it is not
1293    /// a parameter.
1294    pub fn algorithm_hash(&self) -> Option<String> {
1295        self.0.algorithm_hash()
1296    }
1297
1298    /// The key's length in bits for algorithms parameterized by one — the
1299    /// RSA modulus length (WebCrypto's `RsaKeyAlgorithm.modulusLength`).
1300    /// `None` for Ed25519 and ECDSA, whose key size is fixed by the
1301    /// algorithm or curve.
1302    pub fn algorithm_length(&self) -> Option<u32> {
1303        self.0.algorithm_length()
1304    }
1305
1306    /// The RSA public exponent's big-endian bytes (WebCrypto's
1307    /// `RsaKeyAlgorithm.publicExponent`; `[1, 0, 1]` for 65537). `None`
1308    /// for Ed25519 and ECDSA, which have no such parameter.
1309    pub fn algorithm_public_exponent(&self) -> Option<Vec<u8>> {
1310        self.0.algorithm_public_exponent()
1311    }
1312
1313    /// The public key material, in the minting interface's documented
1314    /// public format.
1315    ///
1316    /// There is no extractability gate on this key, so this never fails
1317    /// with [`Error::NotExtractable`]. It can still fail with
1318    /// [`Error::Other`]: a provider may hold the key as a handle it can
1319    /// *use* but not *read*, so verifying succeeds while recovering the
1320    /// encoding does not.
1321    pub async fn export_key_raw(&self) -> Result<Vec<u8>, Error> {
1322        self.0.export_key_raw().await.map_err(Error::from)
1323    }
1324}
1325
1326/// A `signature.signing-key`: private-key signing.
1327pub struct SigningKey(bindings::signature::SigningKey);
1328newtype_common!(SigningKey, bindings::signature::SigningKey, "signing-key");
1329
1330impl SigningKey {
1331    /// Sign `data`, returning the signature in the minting interface's
1332    /// documented wire format.
1333    ///
1334    /// Fails only for operational reasons ([`Error::Other`], or
1335    /// [`Error::Read`] for a failing `DataSource::from_reader` source) —
1336    /// never for misuse, which is unrepresentable.
1337    pub async fn sign(&self, data: impl Into<DataSource<'_>>) -> Result<Vec<u8>, Error> {
1338        run_sourced(data.into(), |rx| self.0.sign(rx)).await
1339    }
1340
1341    /// See [`VerifyingKey::algorithm_name`].
1342    pub fn algorithm_name(&self) -> String {
1343        self.0.algorithm_name()
1344    }
1345
1346    /// See [`VerifyingKey::algorithm_curve`].
1347    pub fn algorithm_curve(&self) -> Option<String> {
1348        self.0.algorithm_curve()
1349    }
1350
1351    /// See [`VerifyingKey::algorithm_hash`].
1352    pub fn algorithm_hash(&self) -> Option<String> {
1353        self.0.algorithm_hash()
1354    }
1355
1356    /// See [`VerifyingKey::algorithm_length`].
1357    pub fn algorithm_length(&self) -> Option<u32> {
1358        self.0.algorithm_length()
1359    }
1360
1361    /// See [`VerifyingKey::algorithm_public_exponent`].
1362    pub fn algorithm_public_exponent(&self) -> Option<Vec<u8>> {
1363        self.0.algorithm_public_exponent()
1364    }
1365
1366    /// Whether the private key material may be exported. There is
1367    /// currently no export operation — extractability is mint-time
1368    /// recorded policy that future format-specific exports and
1369    /// platform-backed key storage honor (see the WIT
1370    /// `signing-key.extractable` doc).
1371    pub fn extractable(&self) -> bool {
1372        self.0.extractable()
1373    }
1374
1375    /// Whether the key permits [`sign`](Self::sign) — the usage recorded
1376    /// at mint (or carried by a platform keystore key). A refused
1377    /// operation fails [`Error::NotPermitted`].
1378    pub fn can_sign(&self) -> bool {
1379        self.0.can_sign()
1380    }
1381}
1382
1383/// A `public-encryption.encryption-key`: the public half of asymmetric
1384/// encryption — encryption and wrapping, secret-free to hold.
1385///
1386/// Operations take and return whole byte buffers rather than
1387/// [`DataSource`]s: the plaintext is bounded by the key (for RSA-OAEP, the
1388/// modulus length minus the padding overhead), so there is nothing
1389/// unbounded to stream. Encryption is randomized — encrypting one
1390/// plaintext twice yields different ciphertexts, and both decrypt.
1391pub struct EncryptionKey(bindings::public_encryption::EncryptionKey);
1392newtype_common!(
1393    EncryptionKey,
1394    bindings::public_encryption::EncryptionKey,
1395    "encryption-key"
1396);
1397
1398impl EncryptionKey {
1399    /// Encrypt a plaintext bounded by the key. `label` is optional context
1400    /// bound into the padding: decryption succeeds only under the same
1401    /// label (WebCrypto's `RsaOaepParams.label`). A plaintext above the
1402    /// key's bound fails [`Error::Extension`] (origin `"polymorph:webcrypto"`,
1403    /// name `"message-too-long"`) — the signal to switch to hybrid
1404    /// wrapping: encrypt a symmetric key, wrap the payload under it.
1405    pub async fn encrypt(
1406        &self,
1407        label: Option<&[u8]>,
1408        plaintext: impl Into<Vec<u8>>,
1409    ) -> Result<Vec<u8>, Error> {
1410        self.0
1411            .encrypt(label.map(<[u8]>::to_vec), plaintext.into())
1412            .await
1413            .map_err(Error::from)
1414    }
1415
1416    /// Wrap key material serialized as a [`WrapInput`]: the material
1417    /// transits neither caller. The serialized form must fit the key's
1418    /// bound — symmetric-key JWKs do, private-key serializations generally
1419    /// do not — else the same `"message-too-long"` extension condition as
1420    /// [`encrypt`](Self::encrypt). Consumes the [`WrapInput`].
1421    pub async fn wrap(&self, label: Option<&[u8]>, input: WrapInput) -> Result<Vec<u8>, Error> {
1422        self.0
1423            .wrap(label.map(<[u8]>::to_vec), input.into_raw())
1424            .await
1425            .map_err(Error::from)
1426    }
1427
1428    /// The registry name of the key's algorithm family, e.g. `"RSA-OAEP"`
1429    /// — WebCrypto's `KeyAlgorithm.name`.
1430    pub fn algorithm_name(&self) -> String {
1431        self.0.algorithm_name()
1432    }
1433
1434    /// The registry name of the digest bound at mint, e.g. `"SHA-256"`.
1435    pub fn algorithm_hash(&self) -> Option<String> {
1436        self.0.algorithm_hash()
1437    }
1438
1439    /// The key's length in bits for algorithms parameterized by one — the
1440    /// RSA modulus length (WebCrypto's `RsaKeyAlgorithm.modulusLength`).
1441    pub fn algorithm_length(&self) -> Option<u32> {
1442        self.0.algorithm_length()
1443    }
1444
1445    /// The public exponent's big-endian bytes (WebCrypto's
1446    /// `RsaKeyAlgorithm.publicExponent`; `[1, 0, 1]` for 65537).
1447    pub fn algorithm_public_exponent(&self) -> Option<Vec<u8>> {
1448        self.0.algorithm_public_exponent()
1449    }
1450
1451    /// The public key material, in the minting interface's documented
1452    /// public format. Algorithms without a raw public form (the RSA
1453    /// family) fail [`Error::Unsupported`].
1454    ///
1455    /// There is no extractability gate on public material, so this never
1456    /// fails [`Error::NotExtractable`] — but it can fail [`Error::Other`]:
1457    /// a provider may hold the key as a handle it can use but not read
1458    /// (see [`VerifyingKey::export_key_raw`]).
1459    pub async fn export_key_raw(&self) -> Result<Vec<u8>, Error> {
1460        self.0.export_key_raw().await.map_err(Error::from)
1461    }
1462
1463    /// The public key as an X.509 SubjectPublicKeyInfo (DER), with the
1464    /// same handle-not-bytes fallibility as
1465    /// [`export_key_raw`](Self::export_key_raw).
1466    pub async fn export_key_spki(&self) -> Result<Vec<u8>, Error> {
1467        self.0.export_key_spki().await.map_err(Error::from)
1468    }
1469
1470    /// The public key as a JWK (JSON text), with the same fallibility as
1471    /// [`export_key_raw`](Self::export_key_raw).
1472    pub async fn export_key_jwk(&self) -> Result<String, Error> {
1473        self.0.export_key_jwk().await.map_err(Error::from)
1474    }
1475}
1476
1477/// A `public-encryption.decryption-key`: the private half of asymmetric
1478/// encryption. [`decrypt`](Self::decrypt) and [`unwrap`](Self::unwrap) are
1479/// one-shot calls on the immutable key, over whole byte buffers (see
1480/// [`EncryptionKey`] for why nothing streams here).
1481pub struct DecryptionKey(bindings::public_encryption::DecryptionKey);
1482newtype_common!(
1483    DecryptionKey,
1484    bindings::public_encryption::DecryptionKey,
1485    "decryption-key"
1486);
1487
1488impl DecryptionKey {
1489    /// Decrypt a ciphertext produced by the matching public key under the
1490    /// same `label`. Fails [`Error::NotPermitted`] without the
1491    /// [`decrypt`](DecryptionKeyOptions::decrypt) grant; every decryption
1492    /// failure is the one detail-free [`Error::AuthenticationFailed`] — a
1493    /// wrong-length ciphertext, damaged padding, and a mismatched label
1494    /// are indistinguishable, as RFC 8017 requires.
1495    pub async fn decrypt(
1496        &self,
1497        label: Option<&[u8]>,
1498        ciphertext: impl Into<Vec<u8>>,
1499    ) -> Result<Vec<u8>, Error> {
1500        self.0
1501            .decrypt(label.map(<[u8]>::to_vec), ciphertext.into())
1502            .await
1503            .map_err(Error::from)
1504    }
1505
1506    /// Decrypt a wrapped key into an [`UnwrapInput`] for a typed unwrap
1507    /// mint: the material never reaches the caller. Fails
1508    /// [`Error::NotPermitted`] without the
1509    /// [`unwrap`](DecryptionKeyOptions::unwrap) grant; failures are
1510    /// otherwise as [`decrypt`](Self::decrypt).
1511    pub async fn unwrap(
1512        &self,
1513        label: Option<&[u8]>,
1514        ciphertext: impl Into<Vec<u8>>,
1515    ) -> Result<UnwrapInput, Error> {
1516        self.0
1517            .unwrap(label.map(<[u8]>::to_vec), ciphertext.into())
1518            .await
1519            .map(UnwrapInput::from_raw)
1520            .map_err(Error::from)
1521    }
1522
1523    /// See [`EncryptionKey::algorithm_name`].
1524    pub fn algorithm_name(&self) -> String {
1525        self.0.algorithm_name()
1526    }
1527
1528    /// See [`EncryptionKey::algorithm_hash`].
1529    pub fn algorithm_hash(&self) -> Option<String> {
1530        self.0.algorithm_hash()
1531    }
1532
1533    /// See [`EncryptionKey::algorithm_length`].
1534    pub fn algorithm_length(&self) -> Option<u32> {
1535        self.0.algorithm_length()
1536    }
1537
1538    /// See [`EncryptionKey::algorithm_public_exponent`].
1539    pub fn algorithm_public_exponent(&self) -> Option<Vec<u8>> {
1540        self.0.algorithm_public_exponent()
1541    }
1542
1543    /// Whether the key permits [`decrypt`](Self::decrypt) — the usage
1544    /// recorded at mint. A refused operation fails
1545    /// [`Error::NotPermitted`].
1546    pub fn can_decrypt(&self) -> bool {
1547        self.0.can_decrypt()
1548    }
1549
1550    /// Whether the key permits [`unwrap`](Self::unwrap). See
1551    /// [`can_decrypt`](Self::can_decrypt).
1552    pub fn can_unwrap(&self) -> bool {
1553        self.0.can_unwrap()
1554    }
1555
1556    /// Whether the export functions may return this key's material (see
1557    /// [`Mac::extractable`]).
1558    pub fn extractable(&self) -> bool {
1559        self.0.extractable()
1560    }
1561
1562    /// The private key as a JWK (JSON text); fails
1563    /// [`Error::NotExtractable`] unless the key was minted extractable.
1564    pub async fn export_key_jwk(&self) -> Result<String, Error> {
1565        self.0.export_key_jwk().await.map_err(Error::from)
1566    }
1567
1568    /// The private key as a PKCS#8 PrivateKeyInfo (DER), behind the same
1569    /// extractability gate as [`export_key_jwk`](Self::export_key_jwk).
1570    pub async fn export_key_pkcs8(&self) -> Result<Vec<u8>, Error> {
1571        self.0.export_key_pkcs8().await.map_err(Error::from)
1572    }
1573
1574    /// The private-key JWK serialization as a [`WrapInput`], for wrapping
1575    /// under another key. Behind the same extractability gate as
1576    /// [`export_key_jwk`](Self::export_key_jwk).
1577    pub async fn to_wrap_input_jwk(&self) -> Result<WrapInput, Error> {
1578        self.0
1579            .to_wrap_input_jwk()
1580            .await
1581            .map(WrapInput::from_raw)
1582            .map_err(Error::from)
1583    }
1584
1585    /// The PKCS#8 serialization as a [`WrapInput`], behind the same gate.
1586    pub async fn to_wrap_input_pkcs8(&self) -> Result<WrapInput, Error> {
1587        self.0
1588            .to_wrap_input_pkcs8()
1589            .await
1590            .map(WrapInput::from_raw)
1591            .map_err(Error::from)
1592    }
1593}
1594
1595// --- key & digest creation -------------------------------------------------------
1596
1597/// An unauthenticated-cipher key (AES-CBC or AES-CTR), minted by
1598/// [`aes_cbc`] / [`aes_ctr`]. **Nothing this key does authenticates**:
1599/// ciphertext is malleable and a successful [`decrypt`](Self::decrypt) is
1600/// not evidence the input is untampered. Default to [`Aead`]; use this
1601/// kind only where an existing format fixes the mode. See the WIT
1602/// `cipher` interface for the full contract.
1603pub struct CipherKey(bindings::cipher::CipherKey);
1604newtype_common!(CipherKey, bindings::cipher::CipherKey, "cipher-key");
1605
1606impl CipherKey {
1607    /// Encrypt `plaintext` under `iv` (for AES-CTR, the initial counter
1608    /// block plus the counter width in bits; AES-CBC callers pass `None`).
1609    /// The caller owns the IV discipline — see the minting interface's
1610    /// Security notes.
1611    pub fn encrypt<'a>(
1612        &'a self,
1613        iv: impl Into<Cow<'a, [u8]>>,
1614        counter_length: Option<u8>,
1615        plaintext: impl Into<DataSource<'a>>,
1616    ) -> Seal<'a> {
1617        let iv = iv.into().into_owned();
1618        Seal::new(
1619            plaintext.into(),
1620            Box::new(move |rx| Box::pin(self.0.encrypt(iv, counter_length, rx))),
1621        )
1622    }
1623
1624    /// Decrypt `ciphertext` under `iv`. The plaintext is unauthenticated:
1625    /// treat it as attacker-influenced data even on success. Malformed
1626    /// input fails [`Error::Other`], deliberately uniform across
1627    /// conditions.
1628    pub async fn decrypt(
1629        &self,
1630        iv: impl Into<Cow<'_, [u8]>>,
1631        counter_length: Option<u8>,
1632        ciphertext: impl Into<DataSource<'_>>,
1633    ) -> Result<StreamReader<u8>, Error> {
1634        let iv = iv.into().into_owned();
1635        run_sourced(ciphertext.into(), |rx| {
1636            self.0.decrypt(iv, counter_length, rx)
1637        })
1638        .await
1639    }
1640
1641    /// The name of the key's algorithm family, e.g. `"AES-CBC"`.
1642    pub fn algorithm_name(&self) -> String {
1643        self.0.algorithm_name()
1644    }
1645
1646    /// The key length in bits.
1647    pub fn algorithm_length(&self) -> u32 {
1648        self.0.algorithm_length()
1649    }
1650
1651    /// The algorithm's IV size in bytes (`16` for the AES modes).
1652    pub fn iv_size(&self) -> u32 {
1653        self.0.iv_size()
1654    }
1655
1656    /// Whether [`export_key_raw`](Self::export_key_raw) may return the key
1657    /// material (see [`Mac::extractable`]).
1658    pub fn extractable(&self) -> bool {
1659        self.0.extractable()
1660    }
1661
1662    /// Whether the key permits [`encrypt`](Self::encrypt).
1663    pub fn can_encrypt(&self) -> bool {
1664        self.0.can_encrypt()
1665    }
1666
1667    /// Whether the key permits [`decrypt`](Self::decrypt).
1668    pub fn can_decrypt(&self) -> bool {
1669        self.0.can_decrypt()
1670    }
1671
1672    /// Whether the key permits [`wrap`](Self::wrap).
1673    pub fn can_wrap(&self) -> bool {
1674        self.0.can_wrap()
1675    }
1676
1677    /// Whether the key permits [`unwrap`](Self::unwrap). See
1678    /// [`can_wrap`](Self::can_wrap).
1679    pub fn can_unwrap(&self) -> bool {
1680        self.0.can_unwrap()
1681    }
1682
1683    /// Encrypt serialized key material under `iv`, exactly as `encrypt`
1684    /// encrypts a message (the WIT `cipher-key.wrap` contract; nothing
1685    /// here authenticates). Consumes the [`WrapInput`].
1686    pub async fn wrap(
1687        &self,
1688        iv: impl Into<Vec<u8>>,
1689        counter_length: Option<u8>,
1690        input: WrapInput,
1691    ) -> Result<Vec<u8>, Error> {
1692        self.0
1693            .wrap(iv.into(), counter_length, input.into_raw())
1694            .await
1695            .map_err(Error::from)
1696    }
1697
1698    /// Decrypt wrapped key material into an [`UnwrapInput`] for a typed
1699    /// unwrap mint (the WIT `cipher-key.unwrap` contract; the result is
1700    /// unauthenticated).
1701    pub async fn unwrap(
1702        &self,
1703        iv: impl Into<Vec<u8>>,
1704        counter_length: Option<u8>,
1705        wrapped: impl Into<Vec<u8>>,
1706    ) -> Result<UnwrapInput, Error> {
1707        self.0
1708            .unwrap(iv.into(), counter_length, wrapped.into())
1709            .await
1710            .map(UnwrapInput::from_raw)
1711            .map_err(Error::from)
1712    }
1713
1714    /// This key's raw material as a [`WrapInput`], behind the same
1715    /// extractability gate as [`export_key_raw`](Self::export_key_raw).
1716    pub async fn to_wrap_input_raw(&self) -> Result<WrapInput, Error> {
1717        self.0
1718            .to_wrap_input_raw()
1719            .await
1720            .map(WrapInput::from_raw)
1721            .map_err(Error::from)
1722    }
1723
1724    /// The JWK serialization as a [`WrapInput`], behind the same gate.
1725    pub async fn to_wrap_input_jwk(&self) -> Result<WrapInput, Error> {
1726        self.0
1727            .to_wrap_input_jwk()
1728            .await
1729            .map(WrapInput::from_raw)
1730            .map_err(Error::from)
1731    }
1732
1733    /// The raw key material, behind the extractability gate.
1734    pub async fn export_key_raw(&self) -> Result<Vec<u8>, Error> {
1735        Ok(self.0.export_key_raw().await?)
1736    }
1737
1738    /// The key as an `oct` JWK, behind the same gate.
1739    pub async fn export_key_jwk(&self) -> Result<String, Error> {
1740        Ok(self.0.export_key_jwk().await?)
1741    }
1742}
1743
1744// --- derivation ------------------------------------------------------------------
1745
1746/// An `hkdf.ikm`: imported input keying material for HKDF, minted by
1747/// [`hkdf::import_ikm`]. Never readable back through the API under any
1748/// grant; the grants recorded at import are copied onto every
1749/// [`DeriveInput`] built on it (via [`hkdf_sha2::prepare`] and friends).
1750pub struct Ikm(bindings::hkdf::Ikm);
1751newtype_common!(Ikm, bindings::hkdf::Ikm, "ikm");
1752
1753impl Ikm {
1754    /// Whether inputs built on this material may yield raw bits. See
1755    /// [`DeriveOptions::derive_bits`].
1756    pub fn can_derive_bits(&self) -> bool {
1757        self.0.can_derive_bits()
1758    }
1759
1760    /// Whether inputs built on this material may mint keys. See
1761    /// [`DeriveOptions::derive_key`].
1762    pub fn can_derive_key(&self) -> bool {
1763        self.0.can_derive_key()
1764    }
1765}
1766
1767/// A `pbkdf2.password`: an imported password, minted by
1768/// [`pbkdf2::import_password`]. Never readable back through the API under
1769/// any grant; the grants recorded at import are copied onto every
1770/// [`DeriveInput`] built on it (via [`pbkdf2_sha2::prepare`] and friends).
1771pub struct Password(bindings::pbkdf2::Password);
1772newtype_common!(Password, bindings::pbkdf2::Password, "password");
1773
1774impl Password {
1775    /// Whether inputs built on this password may yield raw bits. See
1776    /// [`DeriveOptions::derive_bits`].
1777    pub fn can_derive_bits(&self) -> bool {
1778        self.0.can_derive_bits()
1779    }
1780
1781    /// Whether inputs built on this password may mint keys. See
1782    /// [`DeriveOptions::derive_key`].
1783    pub fn can_derive_key(&self) -> bool {
1784        self.0.can_derive_key()
1785    }
1786}
1787
1788/// A `derivation.derive-input`: a fully parameterized derivation — base
1789/// secret plus every parameter, minted by the `prepare` functions
1790/// ([`hkdf_sha2::prepare`], [`pbkdf2_sha2::prepare`], …) and by
1791/// [`AgreementSecretKey::agree`].
1792///
1793/// Consume it through [`derive_bits`](Self::derive_bits) or hand it to a
1794/// target interface's `derive_key` (e.g. [`hmac_sha2::derive_key`],
1795/// [`aes_gcm::derive_key`]); both may run any number of times. While an
1796/// input is live it may hold derivation state of its own (for HKDF, the
1797/// PRK), so a base secret's sensitivity extends to the inputs built on it.
1798pub struct DeriveInput(bindings::derivation::DeriveInput);
1799newtype_common!(
1800    DeriveInput,
1801    bindings::derivation::DeriveInput,
1802    "derive-input"
1803);
1804
1805impl DeriveInput {
1806    /// The derived bits.
1807    ///
1808    /// `length` is in bits — WebCrypto's denomination for this parameter —
1809    /// and must be a multiple of 8 (none of the package's implementations
1810    /// serve sub-byte outputs). `None` means the source's natural output
1811    /// length: an agreement's full shared secret (32 bytes for X25519).
1812    /// KDF sources have none — their output length is a caller choice —
1813    /// so `None` fails [`Error::Other`] there, matching the platform's
1814    /// own null-length behavior.
1815    ///
1816    /// Fails [`Error::NotPermitted`] without the
1817    /// [`derive_bits`](DeriveOptions::derive_bits) grant.
1818    pub async fn derive_bits(&self, length: Option<u32>) -> Result<Vec<u8>, Error> {
1819        self.0.derive_bits(length).await.map_err(Error::from)
1820    }
1821
1822    /// Whether [`derive_bits`](Self::derive_bits) (and minting
1823    /// *extractable* keys) is permitted — the grant copied from the base
1824    /// secret. A refused operation fails [`Error::NotPermitted`].
1825    pub fn can_derive_bits(&self) -> bool {
1826        self.0.can_derive_bits()
1827    }
1828
1829    /// Whether the target interfaces' `derive_key` is permitted. See
1830    /// [`can_derive_bits`](Self::can_derive_bits).
1831    pub fn can_derive_key(&self) -> bool {
1832        self.0.can_derive_key()
1833    }
1834}
1835
1836/// A `key-agreement.public-key`: the exchangeable half of an agreement
1837/// keypair, minted by [`x25519`]'s imports and
1838/// [`generate_key`](x25519::generate_key). Secret-free.
1839pub struct AgreementPublicKey(bindings::key_agreement::PublicKey);
1840newtype_common!(
1841    AgreementPublicKey,
1842    bindings::key_agreement::PublicKey,
1843    "public-key"
1844);
1845
1846impl AgreementPublicKey {
1847    /// The name of the key's algorithm family, e.g. `"X25519"` —
1848    /// WebCrypto's `KeyAlgorithm.name`.
1849    pub fn algorithm_name(&self) -> String {
1850        self.0.algorithm_name()
1851    }
1852
1853    /// The public key material, in the minting interface's documented
1854    /// public format (32 bytes for X25519).
1855    ///
1856    /// There is no extractability gate on public material, so this never
1857    /// fails [`Error::NotExtractable`] — but it can fail [`Error::Other`]:
1858    /// a provider may hold the key as a handle it can use but not read
1859    /// (see [`VerifyingKey::export_key_raw`]).
1860    pub async fn export_key_raw(&self) -> Result<Vec<u8>, Error> {
1861        self.0.export_key_raw().await.map_err(Error::from)
1862    }
1863
1864    /// The public key as a JWK (an RFC 8037 OKP public key for X25519),
1865    /// with the same handle-not-bytes fallibility as
1866    /// [`export_key_raw`](Self::export_key_raw).
1867    pub async fn export_key_jwk(&self) -> Result<String, Error> {
1868        self.0.export_key_jwk().await.map_err(Error::from)
1869    }
1870
1871    /// The public key as an X.509 SubjectPublicKeyInfo (DER), with the
1872    /// same handle-not-bytes fallibility as
1873    /// [`export_key_raw`](Self::export_key_raw).
1874    pub async fn export_key_spki(&self) -> Result<Vec<u8>, Error> {
1875        self.0.export_key_spki().await.map_err(Error::from)
1876    }
1877}
1878
1879/// A `key-agreement.secret-key`: the private half of an agreement keypair.
1880/// [`agree`](Self::agree) is one-shot on the immutable key; the derivation
1881/// state lives in the [`DeriveInput`] it returns.
1882pub struct AgreementSecretKey(bindings::key_agreement::SecretKey);
1883newtype_common!(
1884    AgreementSecretKey,
1885    bindings::key_agreement::SecretKey,
1886    "secret-key"
1887);
1888
1889impl AgreementSecretKey {
1890    /// The shared secret with `peer`, as a [`DeriveInput`] whose grants
1891    /// are copied from this key's mint options.
1892    ///
1893    /// The returned input has a *natural* output length — the agreement's
1894    /// full shared secret — so `derive_bits(None)` returns the whole
1895    /// secret and [`hkdf_sha2::prepare_from`] accepts it as IKM.
1896    ///
1897    /// Fails [`Error::InvalidKey`] if the shared secret is the all-zero
1898    /// value (a small-order `peer`; the mandatory contributory check,
1899    /// performed in constant time) or if `peer` is bound to a different
1900    /// algorithm than this key.
1901    pub async fn agree(&self, peer: &AgreementPublicKey) -> Result<DeriveInput, Error> {
1902        Ok(DeriveInput::from_raw(self.0.agree(&peer.0).await?))
1903    }
1904
1905    /// See [`AgreementPublicKey::algorithm_name`].
1906    pub fn algorithm_name(&self) -> String {
1907        self.0.algorithm_name()
1908    }
1909
1910    /// Whether inputs agreed by this key may yield raw bits. See
1911    /// [`DeriveOptions::derive_bits`].
1912    pub fn can_derive_bits(&self) -> bool {
1913        self.0.can_derive_bits()
1914    }
1915
1916    /// Whether inputs agreed by this key may mint keys. See
1917    /// [`DeriveOptions::derive_key`].
1918    pub fn can_derive_key(&self) -> bool {
1919        self.0.can_derive_key()
1920    }
1921
1922    /// Whether the export functions may return this key's material (see
1923    /// [`Mac::extractable`]).
1924    pub fn extractable(&self) -> bool {
1925        self.0.extractable()
1926    }
1927
1928    /// The secret key as a JWK (an RFC 8037 OKP private key for X25519);
1929    /// fails [`Error::NotExtractable`] unless the key was minted
1930    /// extractable.
1931    pub async fn export_key_jwk(&self) -> Result<String, Error> {
1932        self.0.export_key_jwk().await.map_err(Error::from)
1933    }
1934
1935    /// The secret key as a PKCS#8 PrivateKeyInfo (DER), behind the same
1936    /// extractability gate as [`export_key_jwk`](Self::export_key_jwk).
1937    pub async fn export_key_pkcs8(&self) -> Result<Vec<u8>, Error> {
1938        self.0.export_key_pkcs8().await.map_err(Error::from)
1939    }
1940}
1941
1942/// A `wrapping.wrap-input`: one key's serialized material awaiting
1943/// encryption under a wrapping key. Single-use — the consuming wrap
1944/// operation takes it by value, on failure as on success.
1945pub struct WrapInput(bindings::wrapping::WrapInput);
1946newtype_common!(WrapInput, bindings::wrapping::WrapInput, "wrap-input");
1947
1948/// A `wrapping.unwrap-input`: decrypted key material awaiting a typed
1949/// unwrap mint. Single-use, like [`WrapInput`].
1950pub struct UnwrapInput(bindings::wrapping::UnwrapInput);
1951newtype_common!(UnwrapInput, bindings::wrapping::UnwrapInput, "unwrap-input");
1952
1953/// A `key-wrap.kw-key`: a dedicated key-wrapping key (AES-KW), bound to
1954/// its algorithm at creation.
1955pub struct KwKey(bindings::key_wrap::KwKey);
1956newtype_common!(KwKey, bindings::key_wrap::KwKey, "kw-key");
1957
1958impl KwKey {
1959    /// Encrypt serialized key material (the WIT `kw-key.wrap` contract:
1960    /// deterministic; JWK-formatted input is space-padded to a multiple
1961    /// of 8; the input domain is a multiple of 8 bytes, at least 16).
1962    /// Consumes the [`WrapInput`].
1963    pub async fn wrap(&self, input: WrapInput) -> Result<Vec<u8>, Error> {
1964        self.0.wrap(input.into_raw()).await.map_err(Error::from)
1965    }
1966
1967    /// Decrypt and integrity-check wrapped key material into an
1968    /// [`UnwrapInput`] for a typed unwrap mint. Every integrity failure —
1969    /// including input outside the wrapped-form domain — fails
1970    /// [`Error::AuthenticationFailed`].
1971    pub async fn unwrap(&self, wrapped: impl Into<Vec<u8>>) -> Result<UnwrapInput, Error> {
1972        self.0
1973            .unwrap(wrapped.into())
1974            .await
1975            .map(UnwrapInput::from_raw)
1976            .map_err(Error::from)
1977    }
1978
1979    /// The registry algorithm name, `"AES-KW"`.
1980    pub fn algorithm_name(&self) -> String {
1981        self.0.algorithm_name()
1982    }
1983
1984    /// The key length in bits.
1985    pub fn algorithm_length(&self) -> u32 {
1986        self.0.algorithm_length()
1987    }
1988
1989    /// Whether the key material may be exported.
1990    pub fn extractable(&self) -> bool {
1991        self.0.extractable()
1992    }
1993
1994    /// Whether the key permits [`wrap`](Self::wrap).
1995    pub fn can_wrap(&self) -> bool {
1996        self.0.can_wrap()
1997    }
1998
1999    /// Whether the key permits [`unwrap`](Self::unwrap).
2000    pub fn can_unwrap(&self) -> bool {
2001        self.0.can_unwrap()
2002    }
2003
2004    /// This key's raw material as a [`WrapInput`], behind the same
2005    /// extractability gate as [`export_key_raw`](Self::export_key_raw).
2006    pub async fn to_wrap_input_raw(&self) -> Result<WrapInput, Error> {
2007        self.0
2008            .to_wrap_input_raw()
2009            .await
2010            .map(WrapInput::from_raw)
2011            .map_err(Error::from)
2012    }
2013
2014    /// The JWK serialization as a [`WrapInput`], behind the same gate.
2015    pub async fn to_wrap_input_jwk(&self) -> Result<WrapInput, Error> {
2016        self.0
2017            .to_wrap_input_jwk()
2018            .await
2019            .map(WrapInput::from_raw)
2020            .map_err(Error::from)
2021    }
2022
2023    /// The raw key material, behind the extractability gate.
2024    pub async fn export_key_raw(&self) -> Result<Vec<u8>, Error> {
2025        self.0.export_key_raw().await.map_err(Error::from)
2026    }
2027
2028    /// The key as an `oct` JWK, behind the same gate.
2029    pub async fn export_key_jwk(&self) -> Result<String, Error> {
2030        self.0.export_key_jwk().await.map_err(Error::from)
2031    }
2032}
2033
2034pub mod aes_cbc;
2035pub mod aes_ctr;
2036pub mod aes_gcm;
2037pub mod aes_kw;
2038pub mod ecdh;
2039pub mod ecdsa;
2040pub mod ed25519;
2041pub mod hkdf;
2042pub mod hkdf_sha1;
2043pub mod hkdf_sha2;
2044pub mod hmac_sha1;
2045pub mod hmac_sha2;
2046pub mod pbkdf2;
2047pub mod pbkdf2_sha1;
2048pub mod pbkdf2_sha2;
2049pub mod rsa_oaep;
2050pub mod rsa_pss;
2051pub mod rsassa_pkcs1_v15;
2052#[cfg(feature = "sha1-checked")]
2053pub mod sha1_checked;
2054pub mod sha2;
2055pub mod x25519;
2056
2057#[cfg(test)]
2058mod tests {
2059    use super::{Error, Feed};
2060
2061    fn read_error() -> Error {
2062        Error::Read(std::io::Error::other("reader failed"))
2063    }
2064
2065    /// Rejection maps to [`Error::ShortWrite`] only through the
2066    /// success-path requirement; completion satisfies it. (The rest of
2067    /// the precedence is statement order at the join sites: the feed's
2068    /// `?` — always a read failure — before the operation's, before this
2069    /// requirement.)
2070    #[test]
2071    fn rejection_is_short_write_only_by_requirement() {
2072        assert!(matches!(
2073            Feed::Rejected.require_complete(),
2074            Err(Error::ShortWrite)
2075        ));
2076        assert!(Feed::Complete.require_complete().is_ok());
2077    }
2078
2079    /// Every WIT error case maps onto its own variant — and the match in
2080    /// `From` is exhaustive, so a case added to the WIT is a compile error
2081    /// here rather than a silent fallthrough.
2082    #[test]
2083    fn wit_errors_map_onto_their_variants() {
2084        use super::bindings::types::{Error as Raw, ExtensionError};
2085        assert!(matches!(
2086            Error::from(Raw::InvalidKey("k".into())),
2087            Error::InvalidKey(_)
2088        ));
2089        assert!(matches!(
2090            Error::from(Raw::InvalidNonce("n".into())),
2091            Error::InvalidNonce(_)
2092        ));
2093        assert!(matches!(
2094            Error::from(Raw::AuthenticationFailed),
2095            Error::AuthenticationFailed
2096        ));
2097        assert!(matches!(
2098            Error::from(Raw::NotExtractable),
2099            Error::NotExtractable
2100        ));
2101        assert!(matches!(
2102            Error::from(Raw::Unsupported("u".into())),
2103            Error::Unsupported(_)
2104        ));
2105        assert!(matches!(
2106            Error::from(Raw::NotPermitted("p".into())),
2107            Error::NotPermitted(_)
2108        ));
2109        assert!(matches!(
2110            Error::from(Raw::Other("o".into())),
2111            Error::Other(_)
2112        ));
2113        assert!(matches!(
2114            Error::from(Raw::Extension(ExtensionError {
2115                origin: "polymorph:webcrypto".into(),
2116                name: "collision-detected".into(),
2117                message: "m".into(),
2118            })),
2119            Error::Extension(_)
2120        ));
2121    }
2122
2123    /// Every `Display` rendering identifies its condition: the WIT-mirrored
2124    /// variants by case name, the SDK-local ones by prose.
2125    #[test]
2126    fn display_identifies_every_condition() {
2127        let renders = [
2128            (Error::InvalidKey("k".into()), "invalid-key: k"),
2129            (Error::InvalidNonce("n".into()), "invalid-nonce: n"),
2130            (Error::AuthenticationFailed, "authentication-failed"),
2131            (Error::NotExtractable, "not-extractable"),
2132            (Error::Unsupported("u".into()), "unsupported: u"),
2133            (Error::NotPermitted("p".into()), "not-permitted: p"),
2134            (Error::Other("o".into()), "other: o"),
2135        ];
2136        for (error, expected) in renders {
2137            assert_eq!(error.to_string(), expected);
2138        }
2139        assert!(read_error().to_string().contains("reader failed"));
2140        assert!(Error::ShortWrite.to_string().starts_with("short write"));
2141    }
2142}