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