-
Notifications
You must be signed in to change notification settings - Fork 83
Add async support for DTLS #241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Move `WriteTracker` into `Context` so we could get rid of `AsyncIoAdapter`.
Taowyoo
commented
Mar 28, 2023
Taowyoo
commented
Mar 28, 2023
This reverts commit e1952f6.
jethrogb
reviewed
Mar 30, 2023
Trait `AsyncSend` and `AsyncRecv` are redundant
jjfiv
reviewed
Mar 31, 2023
Better type match
e8fa437
to
1686a70
Compare
bors bot
added a commit
that referenced
this pull request
Apr 19, 2023
239: Add async IO & async support for TLS r=jethrogb a=Taowyoo # TLDR This PR added: - Async version of IO abstraction ( only stream part, IO abstraction is introduced on PR: [Add a new IO abstraction #237](237) ) - Implementation for TLS # Detailed Explanation ## Background We use `mbedtls` for crypto and SSL handshake in rust. `mbedtls::ssl` module contains most FFI wrapper for c mbedtls API. ### How `mbedtls::ssl` works To setup a TLS client or a TLS server with `mbedtls`, mainly you need to provide two things to `mbedtls`: - [Config](https://github.com/fortanix/rust-mbedtls/blob/c39d93a19d043587dc2346fb4c54501e71190162/mbedtls/src/ssl/config.rs#L148): a config object containing settings and parameters needed for TLS handshake - [`BIO`][1]: similar to `openssl`, `mbedtls` need you to provide a `BIO` to it. `BIO` stands for **Basic I/O abstraction**, at here you could simply treat them as source & destination of data such as a TCP socket or file i/o. In code level, it represent as two callbacks in rust: ```rust // representation of the `mbedtls_ssl_send_t` and `mbedtls_ssl_recv_t` callback function pointers. unsafe extern "C" fn call_recv(user_data: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int where Self: Sized; unsafe extern "C" fn call_send(user_data: *mut c_void, data: *const c_uchar, len: size_t) -> c_int where Self: Sized; ``` In `mbedtls`, we pass function pointers and user_data pointer to c mbedtls lib through FFI function `ssl_set_bio`. During handshake and data transfer, the c mbedtls lib will utilize those two callbacks to send or receive data from underlying IO. ### IO abstraction The IO abstraction PR #237 created and implemented safe rust abstraction of `BIO`: <details> <summary>Details</summary> - Introduce unsafe trait [`IoCallbackUnsafe`](https://github.com/fortanix/rust-mbedtls/blob/c39d93a19d043587dc2346fb4c54501e71190162/mbedtls/src/ssl/io.rs#L36) to represent `mbedtls_ssl_send_t` and `mbedtls_ssl_recv_t` callback function pointers. - Introduce safe trait [`IoCallback`](https://github.com/fortanix/rust-mbedtls/blob/c39d93a19d043587dc2346fb4c54501e71190162/mbedtls/src/ssl/io.rs#L48) : A safe representation of above trait. Also provides generic implementation of `IoCallback` for `IoCallbackUnsafe`. So user only need to implement this trait. - Introduce trait [`Io`](https://github.com/fortanix/rust-mbedtls/blob/c39d93a19d043587dc2346fb4c54501e71190162/mbedtls/src/ssl/io.rs#L102) to represent a duplex socket or file descriptor that can be read from or written to in case you cannot use `std::io::Read` and `std::io::Write`. - To integrate TCP, `std::net::TcpStream` implemented `std::io::Read` and `std::io::Write`: - Implemented `IoCallback` with Marker types: `Stream` for `T: std::io::Read + std::io::Write` - Implemented `IoCallbackUnsafe` with Marker types: `Stream` for `Contex<T>` - So you can pass `std::net::TcpStream` directly to [`mbedtls::ssl::context::Context::establish`](https://github.com/fortanix/rust-mbedtls/blob/yx/async-support_master/mbedtls/src/ssl/context.rs#L234) - To integrate UDP, since `std::net::UdpSocket` does not implement and reader/writer trait: - Introduced `ConnectedUdpSocket` as wrapper of `std::net::UdpSocket` and implemented `Io` for it - Implemented `IoCallback` with Marker types: `AnyIo` for `T: Io` - Implemented `IoCallbackUnsafe` with Marker types: `AnyIo` for `Contex<T>` - So you can pass `ConnectedUdpSocket` directly to [`mbedtls::ssl::context::Context::establish`](https://github.com/fortanix/rust-mbedtls/blob/yx/async-support_master/mbedtls/src/ssl/context.rs#L234) </details> ## Async Support for TLS ### Follow same path To add async support, we could use `tokio::net::TcpStream` and traits `tokio::io::Read` and `tokio::io::Write` and follow the same path of what we did for TCP in [IO abstraction](###IO_abstraction): <details> <summary>Details</summary> - (new) Add marker type for async Steam IO: `AsyncStream` - (new) Add [`establish_async`](https://github.com/fortanix/rust-mbedtls/blob/yx/async-support_master/mbedtls/src/ssl/async_io.rs#L144) for `Context<T>`, this async function is implemented by wrapping the original blocking function [`handshake`](https://github.com/fortanix/rust-mbedtls/blob/yx/async-support_master/mbedtls/src/ssl/context.rs#L271) in a custom `Future` implementation. - Implemented `IoCallback` with Marker types: `AsyncStream` for `T: tokio::io::Read + tokio::io::Write` - Implemented `IoCallbackUnsafe` with Marker types: `AsyncStream` for `Contex<T>` - So you can pass `tokio::net::TcpStream` directly to [`Context::establish_async`](https://github.com/fortanix/rust-mbedtls/blob/yx/async-support_master/mbedtls/src/ssl/async_io.rs#L144) </details> ### Async need Context But the key obstacle here is async code need context. The functions needed by [`tokio::io::Read`](https://docs.rs/tokio/latest/tokio/io/trait.AsyncRead.html) and [`tokio::io::Write` ](https://docs.rs/tokio/latest/tokio/io/trait.AsyncWrite.html) both need a parameter: `cx` whose type is `std::task::Context`. To address this, implementation need to adjust: - `IoCallback` `impl`'s now are for a tuple `(&'a mut TaskContext<'b>, &'c mut T)` so that `Context` is accessible in the callback - Introduced function [`with_bio_async`](https://github.com/fortanix/rust-mbedtls/blob/yx/async-support_master/mbedtls/src/ssl/context.rs#L199) to use a callback function to wrap the `Context` and callback functions' data pointer together and pass it to `mbedtls` through `set_bio_raw` **Note**: The async support for DTLS will be added in another PR: #241 [1]: https://www.openssl.org/docs/man1.1.1/man7/bio.html Co-authored-by: Yuxiang Cao <yuxiang.cao@fortanix.com>
bors bot
added a commit
that referenced
this pull request
Apr 19, 2023
239: Add async IO & async support for TLS r=[jethrogb] a=Taowyoo # TLDR This PR added: - Async version of IO abstraction ( only stream part, IO abstraction is introduced on PR: [Add a new IO abstraction #237](237) ) - Implementation for TLS # Detailed Explanation ## Background We use `mbedtls` for crypto and SSL handshake in rust. `mbedtls::ssl` module contains most FFI wrapper for c mbedtls API. ### How `mbedtls::ssl` works To setup a TLS client or a TLS server with `mbedtls`, mainly you need to provide two things to `mbedtls`: - [Config](https://github.com/fortanix/rust-mbedtls/blob/c39d93a19d043587dc2346fb4c54501e71190162/mbedtls/src/ssl/config.rs#L148): a config object containing settings and parameters needed for TLS handshake - [`BIO`][1]: similar to `openssl`, `mbedtls` need you to provide a `BIO` to it. `BIO` stands for **Basic I/O abstraction**, at here you could simply treat them as source & destination of data such as a TCP socket or file i/o. In code level, it represent as two callbacks in rust: ```rust // representation of the `mbedtls_ssl_send_t` and `mbedtls_ssl_recv_t` callback function pointers. unsafe extern "C" fn call_recv(user_data: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int where Self: Sized; unsafe extern "C" fn call_send(user_data: *mut c_void, data: *const c_uchar, len: size_t) -> c_int where Self: Sized; ``` In `mbedtls`, we pass function pointers and user_data pointer to c mbedtls lib through FFI function `ssl_set_bio`. During handshake and data transfer, the c mbedtls lib will utilize those two callbacks to send or receive data from underlying IO. ### IO abstraction The IO abstraction PR #237 created and implemented safe rust abstraction of `BIO`: <details> <summary>Details</summary> - Introduce unsafe trait [`IoCallbackUnsafe`](https://github.com/fortanix/rust-mbedtls/blob/c39d93a19d043587dc2346fb4c54501e71190162/mbedtls/src/ssl/io.rs#L36) to represent `mbedtls_ssl_send_t` and `mbedtls_ssl_recv_t` callback function pointers. - Introduce safe trait [`IoCallback`](https://github.com/fortanix/rust-mbedtls/blob/c39d93a19d043587dc2346fb4c54501e71190162/mbedtls/src/ssl/io.rs#L48) : A safe representation of above trait. Also provides generic implementation of `IoCallback` for `IoCallbackUnsafe`. So user only need to implement this trait. - Introduce trait [`Io`](https://github.com/fortanix/rust-mbedtls/blob/c39d93a19d043587dc2346fb4c54501e71190162/mbedtls/src/ssl/io.rs#L102) to represent a duplex socket or file descriptor that can be read from or written to in case you cannot use `std::io::Read` and `std::io::Write`. - To integrate TCP, `std::net::TcpStream` implemented `std::io::Read` and `std::io::Write`: - Implemented `IoCallback` with Marker types: `Stream` for `T: std::io::Read + std::io::Write` - Implemented `IoCallbackUnsafe` with Marker types: `Stream` for `Contex<T>` - So you can pass `std::net::TcpStream` directly to [`mbedtls::ssl::context::Context::establish`](https://github.com/fortanix/rust-mbedtls/blob/yx/async-support_master/mbedtls/src/ssl/context.rs#L234) - To integrate UDP, since `std::net::UdpSocket` does not implement and reader/writer trait: - Introduced `ConnectedUdpSocket` as wrapper of `std::net::UdpSocket` and implemented `Io` for it - Implemented `IoCallback` with Marker types: `AnyIo` for `T: Io` - Implemented `IoCallbackUnsafe` with Marker types: `AnyIo` for `Contex<T>` - So you can pass `ConnectedUdpSocket` directly to [`mbedtls::ssl::context::Context::establish`](https://github.com/fortanix/rust-mbedtls/blob/yx/async-support_master/mbedtls/src/ssl/context.rs#L234) </details> ## Async Support for TLS ### Follow same path To add async support, we could use `tokio::net::TcpStream` and traits `tokio::io::Read` and `tokio::io::Write` and follow the same path of what we did for TCP in [IO abstraction](###IO_abstraction): <details> <summary>Details</summary> - (new) Add marker type for async Steam IO: `AsyncStream` - (new) Add [`establish_async`](https://github.com/fortanix/rust-mbedtls/blob/yx/async-support_master/mbedtls/src/ssl/async_io.rs#L144) for `Context<T>`, this async function is implemented by wrapping the original blocking function [`handshake`](https://github.com/fortanix/rust-mbedtls/blob/yx/async-support_master/mbedtls/src/ssl/context.rs#L271) in a custom `Future` implementation. - Implemented `IoCallback` with Marker types: `AsyncStream` for `T: tokio::io::Read + tokio::io::Write` - Implemented `IoCallbackUnsafe` with Marker types: `AsyncStream` for `Contex<T>` - So you can pass `tokio::net::TcpStream` directly to [`Context::establish_async`](https://github.com/fortanix/rust-mbedtls/blob/yx/async-support_master/mbedtls/src/ssl/async_io.rs#L144) </details> ### Async need Context But the key obstacle here is async code need context. The functions needed by [`tokio::io::Read`](https://docs.rs/tokio/latest/tokio/io/trait.AsyncRead.html) and [`tokio::io::Write` ](https://docs.rs/tokio/latest/tokio/io/trait.AsyncWrite.html) both need a parameter: `cx` whose type is `std::task::Context`. To address this, implementation need to adjust: - `IoCallback` `impl`'s now are for a tuple `(&'a mut TaskContext<'b>, &'c mut T)` so that `Context` is accessible in the callback - Introduced function [`with_bio_async`](https://github.com/fortanix/rust-mbedtls/blob/yx/async-support_master/mbedtls/src/ssl/context.rs#L199) to use a callback function to wrap the `Context` and callback functions' data pointer together and pass it to `mbedtls` through `set_bio_raw` **Note**: The async support for DTLS will be added in another PR: #241 [1]: https://www.openssl.org/docs/man1.1.1/man7/bio.html Co-authored-by: Yuxiang Cao <yuxiang.cao@fortanix.com>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
To keep things simple, the async support for DTLS is implemented by:
ConnectedAsyncUdpSocket
AsyncRead
andAsyncWrite
forConnectedAsyncUdpSocket
async-session
test for testing DTLS