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