wasmtime_wasi_tls/
lib.rs

1//! # Wasmtime's [wasi-tls] (Transport Layer Security) Implementation
2//!
3//! This crate provides the Wasmtime host implementation for the [wasi-tls] API.
4//! The [wasi-tls] world allows WebAssembly modules to perform SSL/TLS operations,
5//! such as establishing secure connections to servers. TLS often relies on other wasi networking systems
6//! to provide the stream so it will be common to enable the [wasi:cli] world as well with the networking features enabled.
7//!
8//! # An example of how to configure [wasi-tls] is the following:
9//!
10//! ```rust
11//! use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
12//! use wasmtime::{
13//!     component::{Linker, ResourceTable},
14//!     Store, Engine, Result, Config
15//! };
16//! use wasmtime_wasi_tls::{LinkOptions, WasiTls, WasiTlsCtx, WasiTlsCtxBuilder};
17//!
18//! struct Ctx {
19//!     table: ResourceTable,
20//!     wasi_ctx: WasiCtx,
21//!     wasi_tls_ctx: WasiTlsCtx,
22//! }
23//!
24//! impl WasiView for Ctx {
25//!     fn ctx(&mut self) -> WasiCtxView<'_> {
26//!         WasiCtxView { ctx: &mut self.wasi_ctx, table: &mut self.table }
27//!     }
28//! }
29//!
30//! #[tokio::main]
31//! async fn main() -> Result<()> {
32//!     let ctx = Ctx {
33//!         table: ResourceTable::new(),
34//!         wasi_ctx: WasiCtx::builder()
35//!             .inherit_stderr()
36//!             .inherit_network()
37//!             .allow_ip_name_lookup(true)
38//!             .build(),
39//!         wasi_tls_ctx: WasiTlsCtxBuilder::new()
40//!             // Optionally, configure a different TLS provider:
41//!             // .provider(Box::new(wasmtime_wasi_tls_nativetls::NativeTlsProvider::default()))
42//!             .build(),
43//!     };
44//!
45//!     let mut config = Config::new();
46//!     config.async_support(true);
47//!     let engine = Engine::new(&config)?;
48//!
49//!     // Set up wasi-cli
50//!     let mut store = Store::new(&engine, ctx);
51//!     let mut linker = Linker::new(&engine);
52//!     wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
53//!
54//!     // Add wasi-tls types and turn on the feature in linker
55//!     let mut opts = LinkOptions::default();
56//!     opts.tls(true);
57//!     wasmtime_wasi_tls::add_to_linker(&mut linker, &mut opts, |h: &mut Ctx| {
58//!         WasiTls::new(&h.wasi_tls_ctx, &mut h.table)
59//!     })?;
60//!
61//!     // ... use `linker` to instantiate within `store` ...
62//!     Ok(())
63//! }
64//!
65//! ```
66//! [wasi-tls]: https://github.com/WebAssembly/wasi-tls
67//! [wasi:cli]: https://docs.rs/wasmtime-wasi/latest
68
69#![deny(missing_docs)]
70#![doc(test(attr(deny(warnings))))]
71#![doc(test(attr(allow(dead_code, unused_variables, unused_mut))))]
72
73use tokio::io::{AsyncRead, AsyncWrite};
74use wasmtime::component::{HasData, ResourceTable};
75
76pub mod bindings;
77mod host;
78mod io;
79mod rustls;
80
81pub use bindings::types::LinkOptions;
82pub use host::{HostClientConnection, HostClientHandshake, HostFutureClientStreams};
83pub use rustls::RustlsProvider;
84
85/// Capture the state necessary for use in the `wasi-tls` API implementation.
86pub struct WasiTls<'a> {
87    ctx: &'a WasiTlsCtx,
88    table: &'a mut ResourceTable,
89}
90
91impl<'a> WasiTls<'a> {
92    /// Create a new Wasi TLS context
93    pub fn new(ctx: &'a WasiTlsCtx, table: &'a mut ResourceTable) -> Self {
94        Self { ctx, table }
95    }
96}
97
98/// Add the `wasi-tls` world's types to a [`wasmtime::component::Linker`].
99pub fn add_to_linker<T: Send + 'static>(
100    l: &mut wasmtime::component::Linker<T>,
101    opts: &mut LinkOptions,
102    f: fn(&mut T) -> WasiTls<'_>,
103) -> anyhow::Result<()> {
104    bindings::types::add_to_linker::<_, HasWasiTls>(l, &opts, f)?;
105    Ok(())
106}
107
108struct HasWasiTls;
109impl HasData for HasWasiTls {
110    type Data<'a> = WasiTls<'a>;
111}
112
113/// Builder-style structure used to create a [`WasiTlsCtx`].
114pub struct WasiTlsCtxBuilder {
115    provider: Box<dyn TlsProvider>,
116}
117
118impl WasiTlsCtxBuilder {
119    /// Creates a builder for a new context with default parameters set.
120    pub fn new() -> Self {
121        Default::default()
122    }
123
124    /// Configure the TLS provider to use for this context.
125    ///
126    /// By default, this is set to the [`RustlsProvider`].
127    pub fn provider(mut self, provider: Box<dyn TlsProvider>) -> Self {
128        self.provider = provider;
129        self
130    }
131
132    /// Uses the configured context so far to construct the final [`WasiTlsCtx`].
133    pub fn build(self) -> WasiTlsCtx {
134        WasiTlsCtx {
135            provider: self.provider,
136        }
137    }
138}
139impl Default for WasiTlsCtxBuilder {
140    fn default() -> Self {
141        Self {
142            provider: Box::new(RustlsProvider::default()),
143        }
144    }
145}
146
147/// Wasi TLS context needed for internal `wasi-tls` state.
148pub struct WasiTlsCtx {
149    pub(crate) provider: Box<dyn TlsProvider>,
150}
151
152/// The data stream that carries the encrypted TLS data.
153/// Typically this is a TCP stream.
154pub trait TlsTransport: AsyncRead + AsyncWrite + Send + Unpin + 'static {}
155impl<T: AsyncRead + AsyncWrite + Send + Unpin + ?Sized + 'static> TlsTransport for T {}
156
157/// A TLS connection.
158pub trait TlsStream: AsyncRead + AsyncWrite + Send + Unpin + 'static {}
159
160/// A TLS implementation.
161pub trait TlsProvider: Send + Sync + 'static {
162    /// Set up a client TLS connection using the provided `server_name` and `transport`.
163    fn connect(
164        &self,
165        server_name: String,
166        transport: Box<dyn TlsTransport>,
167    ) -> BoxFuture<std::io::Result<Box<dyn TlsStream>>>;
168}
169
170pub(crate) type BoxFuture<T> = std::pin::Pin<Box<dyn Future<Output = T> + Send>>;