Skip to content

Commit 84b3548

Browse files
committed
implement reworked TLS interface
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
1 parent 87aefbe commit 84b3548

File tree

15 files changed

+470
-786
lines changed

15 files changed

+470
-786
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ci/vendor-wit.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ mkdir -p crates/wasi-tls/wit/deps
6565
wkg get --format wit --overwrite "wasi:io@$p2" -o "crates/wasi-tls/wit/deps/io.wit"
6666
get_github wasi-tls v0.2.0-draft+505fc98 crates/wasi-tls/wit/deps/tls
6767

68-
get_github wasi-tls v0.3.0-draft crates/wasi-tls/src/p3/wit/deps/tls
68+
# TODO: Use the tag when released
69+
#get_github wasi-tls v0.3.0-draft crates/wasi-tls/src/p3/wit/deps/tls
6970

7071
rm -rf crates/wasi-config/wit/deps
7172
mkdir -p crates/wasi-config/wit/deps

crates/test-programs/src/bin/p3_tls_sample_application.rs

Lines changed: 82 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1-
use anyhow::{Context as _, Result, anyhow, bail};
2-
use core::future::{Future as _, poll_fn};
3-
use core::pin::pin;
4-
use core::str;
5-
use core::task::{Poll, ready};
1+
use anyhow::{Context as _, Result, anyhow};
2+
use core::future::Future;
63
use futures::try_join;
74
use test_programs::p3::wasi::sockets::ip_name_lookup::resolve_addresses;
85
use test_programs::p3::wasi::sockets::types::{IpAddress, IpSocketAddress, TcpSocket};
9-
use test_programs::p3::wasi::tls;
10-
use test_programs::p3::wasi::tls::client::Hello;
6+
use test_programs::p3::wasi::tls::client::Connector;
117
use test_programs::p3::wit_stream;
12-
use wit_bindgen::StreamResult;
138

149
struct Component;
1510

@@ -27,63 +22,52 @@ async fn test_tls_sample_application(domain: &str, ip: IpAddress) -> Result<()>
2722
.await
2823
.context("tcp connect failed")?;
2924

25+
let conn = Connector::new();
26+
3027
let (sock_rx, sock_rx_fut) = sock.receive();
31-
let hello = Hello::new();
32-
hello
33-
.set_server_name(domain)
34-
.map_err(|()| anyhow!("failed to set SNI"))?;
35-
let (sock_tx, conn) = tls::client::connect(hello, sock_rx);
36-
let sock_tx_fut = sock.send(sock_tx);
37-
38-
let mut conn = pin!(conn.into_future());
39-
let mut sock_rx_fut = pin!(sock_rx_fut.into_future());
40-
let mut sock_tx_fut = pin!(sock_tx_fut);
41-
let conn = poll_fn(|cx| match conn.as_mut().poll(cx) {
42-
Poll::Ready(Ok(conn)) => Poll::Ready(Ok(conn)),
43-
Poll::Ready(Err(())) => Poll::Ready(Err(anyhow!("tls handshake failed"))),
44-
Poll::Pending => match sock_tx_fut.as_mut().poll(cx) {
45-
Poll::Ready(Ok(())) => Poll::Ready(Err(anyhow!("Tx stream closed unexpectedly"))),
46-
Poll::Ready(Err(err)) => {
47-
Poll::Ready(Err(anyhow!("Tx stream closed with error: {err:?}")))
48-
}
49-
Poll::Pending => match ready!(sock_rx_fut.as_mut().poll(cx)) {
50-
Ok(_) => Poll::Ready(Err(anyhow!("Rx stream closed unexpectedly"))),
51-
Err(err) => Poll::Ready(Err(anyhow!("Rx stream closed with error: {err:?}"))),
52-
},
53-
},
54-
})
55-
.await?;
28+
let (tls_rx, tls_rx_fut) = conn.receive(sock_rx);
5629

57-
let (mut req_tx, req_rx) = wit_stream::new();
58-
let (mut res_rx, result_fut) = tls::client::Handshake::finish(conn, req_rx);
30+
let (mut data_tx, data_rx) = wit_stream::new();
31+
let (tls_tx, tls_tx_err_fut) = conn.send(data_rx);
32+
let sock_tx_fut = sock.send(tls_tx);
5933

60-
let res = Vec::with_capacity(8192);
6134
try_join!(
6235
async {
63-
let buf = req_tx.write_all(request.into()).await;
64-
assert_eq!(buf, []);
65-
drop(req_tx);
36+
Connector::connect(conn, domain.into())
37+
.await
38+
.map_err(|err| {
39+
anyhow!(err.to_debug_string()).context("failed to establish connection")
40+
})
41+
},
42+
async {
43+
let buf = data_tx.write_all(request.into()).await;
44+
assert!(buf.is_empty());
45+
drop(data_tx);
6646
Ok(())
6747
},
6848
async {
69-
let (result, buf) = res_rx.read(res).await;
70-
match result {
71-
StreamResult::Complete(..) => {
72-
drop(res_rx);
73-
let res = String::from_utf8(buf)?;
74-
if res.contains("HTTP/1.1 200 OK") {
75-
Ok(())
76-
} else {
77-
bail!("server did not respond with 200 OK: {res}")
78-
}
79-
}
80-
StreamResult::Dropped => bail!("read dropped"),
81-
StreamResult::Cancelled => bail!("read cancelled"),
49+
let response = tls_rx.collect().await;
50+
let response = String::from_utf8(response)?;
51+
if response.contains("HTTP/1.1 200 OK") {
52+
Ok(())
53+
} else {
54+
Err(anyhow!("server did not respond with 200 OK: {response}"))
8255
}
8356
},
84-
async { result_fut.await.map_err(|()| anyhow!("TLS session failed")) },
85-
async { sock_rx_fut.await.context("TCP receipt failed") },
86-
async { sock_tx_fut.await.context("TCP transmit failed") },
57+
async { sock_rx_fut.await.context("failed to receive ciphertext") },
58+
async { sock_tx_fut.await.context("failed to send ciphertext") },
59+
async {
60+
tls_rx_fut
61+
.await
62+
.map_err(|err| anyhow!(err.to_debug_string()))
63+
.context("failed to receive plaintext")
64+
},
65+
async {
66+
tls_tx_err_fut
67+
.await
68+
.map_err(|err| anyhow!(err.to_debug_string()))
69+
.context("failed to send plaintext")
70+
},
8771
)?;
8872
Ok(())
8973
}
@@ -92,32 +76,57 @@ async fn test_tls_sample_application(domain: &str, ip: IpAddress) -> Result<()>
9276
/// perform a TLS handshake using another unrelated domain. This should result
9377
/// in a handshake error.
9478
async fn test_tls_invalid_certificate(_domain: &str, ip: IpAddress) -> Result<()> {
95-
const BAD_DOMAIN: &'static str = "wrongdomain.localhost";
79+
const BAD_DOMAIN: &str = "wrongdomain.localhost";
9680

9781
let sock = TcpSocket::create(ip.family()).unwrap();
9882
sock.connect(IpSocketAddress::new(ip, PORT))
9983
.await
10084
.context("tcp connect failed")?;
10185

86+
let conn = Connector::new();
87+
10288
let (sock_rx, sock_rx_fut) = sock.receive();
103-
let hello = Hello::new();
104-
hello
105-
.set_server_name(BAD_DOMAIN)
106-
.map_err(|()| anyhow!("failed to set SNI"))?;
107-
let (sock_tx, conn) = tls::client::connect(hello, sock_rx);
108-
let sock_tx_fut = sock.send(sock_tx);
89+
let (tls_rx, tls_rx_fut) = conn.receive(sock_rx);
10990

110-
try_join!(
91+
let (_, data_rx) = wit_stream::new();
92+
let (tls_tx, tls_tx_err_fut) = conn.send(data_rx);
93+
let sock_tx_fut = sock.send(tls_tx);
94+
let res = try_join!(
11195
async {
112-
match conn.await {
113-
Err(()) => Ok(()),
114-
Ok(_) => panic!("expecting server name mismatch"),
115-
}
96+
Connector::connect(conn, BAD_DOMAIN.into())
97+
.await
98+
.expect("`connect` failed");
99+
Ok(())
116100
},
117-
async { sock_rx_fut.await.context("TCP receipt failed") },
118-
async { sock_tx_fut.await.context("TCP transmit failed") },
119-
)?;
120-
Ok(())
101+
async {
102+
let response = tls_rx.collect().await;
103+
assert_eq!(response, []);
104+
Ok(())
105+
},
106+
async {
107+
sock_rx_fut.await.expect("failed to receive ciphertext");
108+
Ok(())
109+
},
110+
async {
111+
sock_tx_fut.await.expect("failed to send ciphertext");
112+
Ok(())
113+
},
114+
async { tls_rx_fut.await },
115+
async { tls_tx_err_fut.await },
116+
);
117+
match res {
118+
Err(e) => {
119+
let debug_string = e.to_debug_string();
120+
// We're expecting an error regarding certificates in some form or
121+
// another. When we add more TLS backends this naive check will
122+
// likely need to be revisited/expanded:
123+
if debug_string.contains("certificate") || debug_string.contains("HandshakeFailure") {
124+
return Ok(());
125+
}
126+
Err(anyhow!(debug_string))
127+
}
128+
Ok(_) => panic!("expecting server name mismatch"),
129+
}
121130
}
122131

123132
async fn try_live_endpoints<'a, Fut>(test: impl Fn(&'a str, IpAddress) -> Fut)
@@ -126,7 +135,7 @@ where
126135
{
127136
// since this is testing remote endpoints to ensure system cert store works
128137
// the test uses a couple different endpoints to reduce the number of flakes
129-
const DOMAINS: &'static [&'static str] = &[
138+
const DOMAINS: &[&str] = &[
130139
"example.com",
131140
"api.github.com",
132141
"docs.wasmtime.dev",
@@ -141,7 +150,7 @@ where
141150
.first()
142151
.map(|a| a.to_owned())
143152
.ok_or_else(|| anyhow!("DNS lookup failed."))?;
144-
test(&domain, ip).await
153+
test(domain, ip).await
145154
})();
146155

147156
match result.await {

crates/wasi-tls/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,4 @@ test-programs-artifacts = { workspace = true }
3636
wasmtime-wasi = { workspace = true }
3737
tokio = { workspace = true, features = ["macros"] }
3838
futures = { workspace = true }
39+
test-log = { workspace = true }

crates/wasi-tls/src/p3/bindings.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,13 @@ mod generated {
66
path: "src/p3/wit",
77
world: "wasi:tls/imports",
88
imports: {
9-
"wasi:tls/client.[static]handshake.finish": trappable | tracing | store,
10-
"wasi:tls/client.connect": trappable | tracing | store,
11-
"wasi:tls/server.[static]handshake.finish": trappable | tracing | store,
9+
"wasi:tls/client.[method]connector.receive": trappable | tracing | store,
10+
"wasi:tls/client.[method]connector.send": trappable | tracing | store,
1211
default: trappable | tracing
1312
},
1413
with: {
15-
"wasi:tls/client.handshake": crate::p3::ClientHandshake,
16-
"wasi:tls/client.hello": crate::p3::ClientHello,
17-
"wasi:tls/server.handshake": crate::p3::ServerHandshake,
18-
"wasi:tls/types.certificate": crate::p3::Certificate,
14+
"wasi:tls/client.connector": crate::p3::Connector,
15+
"wasi:tls/types.error": String,
1916
},
2017
});
2118
}

0 commit comments

Comments
 (0)