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