async-tls-0.13.0/.cargo_vcs_info.json0000644000000001360000000000100130320ustar { "git": { "sha1": "245641a14a7315a71e2096f4fff8e9e6718c88fe" }, "path_in_vcs": "" }async-tls-0.13.0/.gitignore000064400000000000000000000000221046102023000136040ustar 00000000000000target Cargo.lock async-tls-0.13.0/.travis.yml000064400000000000000000000011371046102023000137350ustar 00000000000000language: rust cache: cargo matrix: include: - rust: stable os: linux - rust: nightly os: linux - rust: stable os: osx - rust: nightly os: osx script: - | if [[ "$TRAVIS_RUST_VERSION" == stable ]] then rustup component add rustfmt cargo fmt --all -- --check fi - cargo test - cargo test --features early-data - cargo test ---no-default-features --features client - cargo test ---no-default-features --features server - cd examples/server - cargo check - cd ../../examples/client - cargo check async-tls-0.13.0/Cargo.toml0000644000000036600000000000100110350ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "async-tls" version = "0.13.0" authors = [ "The async-rs developers", "Florian Gilcher ", "dignifiedquire ", "quininer kel ", ] description = "Asynchronous TLS/SSL streams using Rustls." homepage = "https://github.com/async-std/async-tls" documentation = "https://docs.rs/async-tls" readme = "README.md" categories = [ "asynchronous", "cryptography", "network-programming", ] license = "MIT/Apache-2.0" repository = "https://github.com/async-std/async-tls" [[test]] name = "test" required-features = [ "client", "server", ] [[test]] name = "google" required-features = ["client"] [dependencies.futures-core] version = "0.3.5" [dependencies.futures-io] version = "0.3.5" [dependencies.rustls] version = "0.21" [dependencies.rustls-pemfile] version = "1.0" [dependencies.rustls-webpki] version = "0.101.4" optional = true [dependencies.webpki-roots] version = "0.22.3" optional = true [dev-dependencies.async-std] version = "1.11" features = ["unstable"] [dev-dependencies.futures-executor] version = "0.3.5" [dev-dependencies.futures-util] version = "0.3.5" features = ["io"] [dev-dependencies.lazy_static] version = "1" [features] client = ["webpki-roots"] default = [ "client", "server", ] early-data = [] server = [] [badges.appveyor] repository = "async-std/async-tls" [badges.travis-ci] repository = "async-std/async-tls" async-tls-0.13.0/Cargo.toml.orig0000644000000025150000000000100117720ustar [package] name = "async-tls" version = "0.13.0" authors = [ "The async-rs developers", "Florian Gilcher ", "dignifiedquire ", "quininer kel ", ] license = "MIT/Apache-2.0" repository = "https://github.com/async-std/async-tls" homepage = "https://github.com/async-std/async-tls" documentation = "https://docs.rs/async-tls" readme = "README.md" description = "Asynchronous TLS/SSL streams using Rustls." categories = ["asynchronous", "cryptography", "network-programming"] edition = "2018" [badges] travis-ci = { repository = "async-std/async-tls" } appveyor = { repository = "async-std/async-tls" } [dependencies] futures-io = "0.3.5" futures-core = "0.3.5" rustls = "0.21" rustls-pemfile = "1.0" # webpki = { version = "0.22.0", optional = true } rustls-webpki = { version = "0.101.4", optional = true } webpki-roots = { version = "0.22.3", optional = true } [features] default = ["client", "server"] client = ["webpki-roots"] early-data = [] server = [] [dev-dependencies] lazy_static = "1" futures-executor = "0.3.5" futures-util = { version = "0.3.5", features = ["io"] } async-std = { version = "1.11", features = ["unstable"] } [[test]] name = "test" required-features = ["client", "server"] [[test]] name = "google" required-features = ["client"] async-tls-0.13.0/Cargo.toml.orig000064400000000000000000000025151046102023000145140ustar 00000000000000[package] name = "async-tls" version = "0.13.0" authors = [ "The async-rs developers", "Florian Gilcher ", "dignifiedquire ", "quininer kel ", ] license = "MIT/Apache-2.0" repository = "https://github.com/async-std/async-tls" homepage = "https://github.com/async-std/async-tls" documentation = "https://docs.rs/async-tls" readme = "README.md" description = "Asynchronous TLS/SSL streams using Rustls." categories = ["asynchronous", "cryptography", "network-programming"] edition = "2018" [badges] travis-ci = { repository = "async-std/async-tls" } appveyor = { repository = "async-std/async-tls" } [dependencies] futures-io = "0.3.5" futures-core = "0.3.5" rustls = "0.21" rustls-pemfile = "1.0" # webpki = { version = "0.22.0", optional = true } rustls-webpki = { version = "0.101.4", optional = true } webpki-roots = { version = "0.22.3", optional = true } [features] default = ["client", "server"] client = ["webpki-roots"] early-data = [] server = [] [dev-dependencies] lazy_static = "1" futures-executor = "0.3.5" futures-util = { version = "0.3.5", features = ["io"] } async-std = { version = "1.11", features = ["unstable"] } [[test]] name = "test" required-features = ["client", "server"] [[test]] name = "google" required-features = ["client"] async-tls-0.13.0/LICENSE-APACHE000064400000000000000000000251201046102023000135460ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2017 quininer kel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. async-tls-0.13.0/LICENSE-MIT000064400000000000000000000020401046102023000132520ustar 00000000000000Copyright (c) 2017 quininer kel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. async-tls-0.13.0/README.md000064400000000000000000000103021046102023000130750ustar 00000000000000

async-tls

Async TLS/SSL streams using Rustls.


# Maintenance Notice I, [Florian Gilcher](https://github.com/skade), am currently the main maintainer of `async-tls`. Due to general workload, I would like the hand the maintenance off to someone else carrying the general vision of `async-tls`: * Runtime-independence * Backend-independence (currently not implemented) * Focused on _use-case based_ configuration rather then exposing all configurations I expect applicants for maintainership to present a committment of at least 4h of time/week initially, so I recommend getting corporate backing first. The number isn't strict, but I want the process to improve maintenance. I'm happy with a group of multiple maintainers. I commit to weekly feedback of 30 minutes for the first 2 months - more in the initial days of handover. If interested, please reach out to me at [through email](mailto:flo@andersground.net). # Features `async-tls` can be used both in server and client programs. To save compilation times, you can switch off parts of this for faster compile times. To only use async-tls on a client, deactivate default features and use the "client" feature. ```toml [dependencies.async-tls] version = "0.8" default-features = false features = ["client"] ``` To only use async-tls on for the server side, deactivate default features and use the "server" feature. ```toml [dependencies.async-tls] version = "0.8" default-features = false features = ["server"] ``` ### Simple Client ```rust use async_tls::TlsConnector; use async_std::net::TcpStream; // ... let tcp_stream = TcpStream::connect("rust-lang.org:443").await?; let connector = TlsConnector::default(); let mut tls_stream = connector.connect("www.rust-lang.org", tcp_stream).await?; // ... ``` ### Client Example Program See [examples/client](examples/client/src/main.rs). You can run it with: ```sh cd examples/client cargo run -- hsts.badssl.com ``` ### Server Example Program See [examples/server](examples/server/src/main.rs). You can run it with: ```sh cd examples/server cargo run -- 127.0.0.1:8080 --cert ../../tests/end.cert --key ../../tests/end.rsa ``` and point the client at it with: ```sh cd examples/client cargo run -- 127.0.0.1 --port 8080 --domain localhost --cafile ../../tests/end.chain ``` **NOTE**: Don't ever use those certificate files anywhere but for testing! ## Safety This crate uses ``#![deny(unsafe_code)]`` to ensure everything is implemented in 100% Safe Rust. ### License & Origin This project is licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. This started as a fork of [tokio-rustls](https://github.com/quininer/tokio-rustls). ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in async-tls by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. async-tls-0.13.0/rustfmt.toml000064400000000000000000000000201046102023000142130ustar 00000000000000edition = "2018"async-tls-0.13.0/src/acceptor.rs000064400000000000000000000050661046102023000145660ustar 00000000000000use crate::common::tls_state::TlsState; use crate::server; use futures_io::{AsyncRead, AsyncWrite}; use rustls::{ServerConfig, ServerConnection}; use std::future::Future; use std::io; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; /// The TLS accepting part. The acceptor drives /// the server side of the TLS handshake process. It works /// on any asynchronous stream. /// /// It provides a simple interface (`accept`), returning a future /// that will resolve when the handshake process completed. On /// success, it will hand you an async `TLSStream`. /// /// ## Example /// /// See /examples/server for an example. #[derive(Clone)] pub struct TlsAcceptor { inner: Arc, } impl TlsAcceptor { /// Accept a client connections. `stream` can be any type implementing `AsyncRead` and `AsyncWrite`, /// such as TcpStreams or Unix domain sockets. /// /// Otherwise, it will return a `Accept` Future, representing the Acceptance part of a /// Tls handshake. It will resolve when the handshake is over. #[inline] pub fn accept(&self, stream: IO) -> Accept where IO: AsyncRead + AsyncWrite + Unpin, { self.accept_with(stream, |_| ()) } // Currently private, as exposing ServerConnections exposes rusttls fn accept_with(&self, stream: IO, f: F) -> Accept where IO: AsyncRead + AsyncWrite + Unpin, F: FnOnce(&mut ServerConnection), { let mut conn = match ServerConnection::new(self.inner.clone()) { Ok(conn) => conn, Err(_) => { return Accept(server::MidHandshake::End); } }; f(&mut conn); Accept(server::MidHandshake::Handshaking(server::TlsStream { conn, io: stream, state: TlsState::Stream, })) } } /// Future returned from `TlsAcceptor::accept` which will resolve /// once the accept handshake has finished. pub struct Accept(server::MidHandshake); impl Future for Accept { type Output = io::Result>; #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.0).poll(cx) } } impl From> for TlsAcceptor { fn from(inner: Arc) -> TlsAcceptor { TlsAcceptor { inner } } } impl From for TlsAcceptor { fn from(inner: ServerConfig) -> TlsAcceptor { TlsAcceptor { inner: Arc::new(inner), } } } async-tls-0.13.0/src/client.rs000064400000000000000000000161221046102023000142370ustar 00000000000000//! The client end of a TLS connection. use crate::common::tls_state::TlsState; use crate::rusttls::stream::Stream; use futures_core::ready; use futures_io::{AsyncRead, AsyncWrite}; use rustls::ClientConnection; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::{io, mem}; /// The client end of a TLS connection. Can be used like any other bidirectional IO stream. /// Wraps the underlying TCP stream. #[derive(Debug)] pub struct TlsStream { pub(crate) io: IO, pub(crate) session: ClientConnection, pub(crate) state: TlsState, #[cfg(feature = "early-data")] pub(crate) early_data: (usize, Vec), } pub(crate) enum MidHandshake { Handshaking(TlsStream), #[cfg(feature = "early-data")] EarlyData(TlsStream), End, } impl TlsStream { /// Returns a reference to the underlying IO stream. pub fn get_ref(&self) -> &IO { &self.io } /// Returns a mutuable reference to the underlying IO stream. pub fn get_mut(&mut self) -> &mut IO { &mut self.io } } impl Future for MidHandshake where IO: AsyncRead + AsyncWrite + Unpin, { type Output = io::Result>; #[inline] fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); if let MidHandshake::Handshaking(stream) = this { let eof = !stream.state.readable(); let (io, session) = (&mut stream.io, &mut stream.session); let mut stream = Stream::new(io, session).set_eof(eof); if stream.conn.is_handshaking() { ready!(stream.complete_io(cx))?; } if stream.conn.wants_write() { ready!(stream.complete_io(cx))?; } } match mem::replace(this, MidHandshake::End) { MidHandshake::Handshaking(stream) => Poll::Ready(Ok(stream)), #[cfg(feature = "early-data")] MidHandshake::EarlyData(stream) => Poll::Ready(Ok(stream)), MidHandshake::End => panic!(), } } } impl AsyncRead for TlsStream where IO: AsyncRead + AsyncWrite + Unpin, { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { match self.state { #[cfg(feature = "early-data")] TlsState::EarlyData => { let this = self.get_mut(); let is_handshaking = this.session.is_handshaking(); let is_early_data_accepted = this.session.is_early_data_accepted(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); let (pos, data) = &mut this.early_data; // complete handshake if is_handshaking { ready!(stream.complete_io(cx))?; } // write early data (fallback) if !is_early_data_accepted { while *pos < data.len() { let len = ready!(stream.as_mut_pin().poll_write(cx, &data[*pos..]))?; *pos += len; } } // end this.state = TlsState::Stream; data.clear(); Pin::new(this).poll_read(cx, buf) } TlsState::Stream | TlsState::WriteShutdown => { let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); match stream.as_mut_pin().poll_read(cx, buf) { Poll::Ready(Ok(0)) => { this.state.shutdown_read(); Poll::Ready(Ok(0)) } Poll::Ready(Ok(n)) => Poll::Ready(Ok(n)), Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::ConnectionAborted => { this.state.shutdown_read(); if this.state.writeable() { stream.conn.send_close_notify(); this.state.shutdown_write(); } Poll::Ready(Ok(0)) } Poll::Ready(Err(err)) => Poll::Ready(Err(err)), Poll::Pending => Poll::Pending, } } TlsState::ReadShutdown | TlsState::FullyShutdown => Poll::Ready(Ok(0)), } } } impl AsyncWrite for TlsStream where IO: AsyncRead + AsyncWrite + Unpin, { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); match this.state { #[cfg(feature = "early-data")] TlsState::EarlyData => { use std::io::Write; let (pos, data) = &mut this.early_data; // write early data if let Some(mut early_data) = stream.conn.client_early_data() { let len = match early_data.write(buf) { Ok(n) => n, Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { return Poll::Pending } Err(err) => return Poll::Ready(Err(err)), }; data.extend_from_slice(&buf[..len]); return Poll::Ready(Ok(len)); } // complete handshake if stream.conn.is_handshaking() { ready!(stream.complete_io(cx))?; } // write early data (fallback) if !stream.conn.is_early_data_accepted() { while *pos < data.len() { let len = ready!(stream.as_mut_pin().poll_write(cx, &data[*pos..]))?; *pos += len; } } // end this.state = TlsState::Stream; data.clear(); stream.as_mut_pin().poll_write(cx, buf) } _ => stream.as_mut_pin().poll_write(cx, buf), } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); stream.as_mut_pin().poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.state.writeable() { self.session.send_close_notify(); self.state.shutdown_write(); } let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); stream.as_mut_pin().poll_close(cx) } } async-tls-0.13.0/src/common/mod.rs000064400000000000000000000000321046102023000150210ustar 00000000000000pub(crate) mod tls_state; async-tls-0.13.0/src/common/tls_state.rs000064400000000000000000000017641046102023000162610ustar 00000000000000#[derive(Debug, Copy, Clone)] pub(crate) enum TlsState { #[cfg(feature = "early-data")] EarlyData, Stream, ReadShutdown, WriteShutdown, FullyShutdown, } impl TlsState { pub(crate) fn shutdown_read(&mut self) { match *self { TlsState::WriteShutdown | TlsState::FullyShutdown => *self = TlsState::FullyShutdown, _ => *self = TlsState::ReadShutdown, } } pub(crate) fn shutdown_write(&mut self) { match *self { TlsState::ReadShutdown | TlsState::FullyShutdown => *self = TlsState::FullyShutdown, _ => *self = TlsState::WriteShutdown, } } pub(crate) fn writeable(&self) -> bool { match *self { TlsState::WriteShutdown | TlsState::FullyShutdown => false, _ => true, } } pub(crate) fn readable(self) -> bool { match self { TlsState::ReadShutdown | TlsState::FullyShutdown => false, _ => true, } } } async-tls-0.13.0/src/connector.rs000064400000000000000000000137731046102023000147640ustar 00000000000000use crate::common::tls_state::TlsState; use crate::client; use futures_io::{AsyncRead, AsyncWrite}; use rustls::{ClientConfig, ClientConnection, OwnedTrustAnchor, RootCertStore, ServerName}; use std::convert::TryFrom; use std::future::Future; use std::io; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; /// The TLS connecting part. The acceptor drives /// the client side of the TLS handshake process. It works /// on any asynchronous stream. /// /// It provides a simple interface (`connect`), returning a future /// that will resolve when the handshake process completed. On /// success, it will hand you an async `TlsStream`. /// /// To create a `TlsConnector` with a non-default configuation, create /// a `rusttls::ClientConfig` and call `.into()` on it. /// /// ## Example /// /// ```rust /// use async_tls::TlsConnector; /// /// async_std::task::block_on(async { /// let connector = TlsConnector::default(); /// let tcp_stream = async_std::net::TcpStream::connect("example.com").await?; /// let encrypted_stream = connector.connect("example.com", tcp_stream).await?; /// /// Ok(()) as async_std::io::Result<()> /// }); /// ``` #[derive(Clone)] pub struct TlsConnector { inner: Arc, #[cfg(feature = "early-data")] early_data: bool, } impl From> for TlsConnector { fn from(inner: Arc) -> TlsConnector { TlsConnector { inner, #[cfg(feature = "early-data")] early_data: false, } } } impl From for TlsConnector { fn from(inner: ClientConfig) -> TlsConnector { TlsConnector { inner: Arc::new(inner), #[cfg(feature = "early-data")] early_data: false, } } } impl Default for TlsConnector { fn default() -> Self { let mut root_certs = RootCertStore::empty(); root_certs.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) })); let config = ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_certs) .with_no_client_auth(); Arc::new(config).into() } } impl TlsConnector { /// Create a new TlsConnector with default configuration. /// /// This is the same as calling `TlsConnector::default()`. pub fn new() -> Self { Default::default() } /// Enable 0-RTT. /// /// You must also set `enable_early_data` to `true` in `ClientConfig`. #[cfg(feature = "early-data")] pub fn early_data(mut self, flag: bool) -> TlsConnector { self.early_data = flag; self } /// Connect to a server. `stream` can be any type implementing `AsyncRead` and `AsyncWrite`, /// such as TcpStreams or Unix domain sockets. /// /// The function will return a `Connect` Future, representing the connecting part of a Tls /// handshake. It will resolve when the handshake is over. #[inline] pub fn connect<'a, IO>(&self, domain: impl AsRef, stream: IO) -> Connect where IO: AsyncRead + AsyncWrite + Unpin, { self.connect_with(domain, stream, |_| ()) } // NOTE: Currently private, exposing ClientConnection exposes rusttls // Early data should be exposed differently fn connect_with<'a, IO, F>(&self, domain: impl AsRef, stream: IO, f: F) -> Connect where IO: AsyncRead + AsyncWrite + Unpin, F: FnOnce(&mut ClientConnection), { let domain = match ServerName::try_from(domain.as_ref()) { Ok(domain) => domain, Err(_) => { return Connect(ConnectInner::Error(Some(io::Error::new( io::ErrorKind::InvalidInput, "invalid domain", )))) } }; let mut session = match ClientConnection::new(self.inner.clone(), domain) { Ok(session) => session, Err(_) => { return Connect(ConnectInner::Error(Some(io::Error::new( io::ErrorKind::Other, "invalid connection", )))) } }; f(&mut session); #[cfg(not(feature = "early-data"))] { Connect(ConnectInner::Handshake(client::MidHandshake::Handshaking( client::TlsStream { session, io: stream, state: TlsState::Stream, }, ))) } #[cfg(feature = "early-data")] { Connect(ConnectInner::Handshake(if self.early_data { client::MidHandshake::EarlyData(client::TlsStream { session, io: stream, state: TlsState::EarlyData, early_data: (0, Vec::new()), }) } else { client::MidHandshake::Handshaking(client::TlsStream { session, io: stream, state: TlsState::Stream, early_data: (0, Vec::new()), }) })) } } } /// Future returned from `TlsConnector::connect` which will resolve /// once the connection handshake has finished. pub struct Connect(ConnectInner); enum ConnectInner { Error(Option), Handshake(client::MidHandshake), } impl Future for Connect { type Output = io::Result>; #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.0 { ConnectInner::Error(ref mut err) => { Poll::Ready(Err(err.take().expect("Polled twice after being Ready"))) } ConnectInner::Handshake(ref mut handshake) => Pin::new(handshake).poll(cx), } } } async-tls-0.13.0/src/lib.rs000064400000000000000000000010631046102023000135250ustar 00000000000000//! Asynchronous TLS/SSL streams for async-std and AsyncRead/AsyncWrite sockets using [rustls](https://github.com/ctz/rustls). #![deny(unsafe_code)] #[cfg(feature = "server")] mod acceptor; #[cfg(feature = "client")] pub mod client; mod common; #[cfg(feature = "client")] mod connector; mod rusttls; #[cfg(feature = "server")] pub mod server; #[cfg(feature = "server")] pub use acceptor::{Accept, TlsAcceptor}; #[cfg(feature = "client")] pub use connector::{Connect, TlsConnector}; #[cfg(all(test, feature = "client", feature = "early-data"))] mod test_0rtt; async-tls-0.13.0/src/rusttls/mod.rs000064400000000000000000000000271046102023000152550ustar 00000000000000pub(crate) mod stream; async-tls-0.13.0/src/rusttls/stream.rs000064400000000000000000000266561046102023000160110ustar 00000000000000use futures_core::ready; use futures_io::{AsyncRead, AsyncWrite}; #[cfg(feature = "early-data")] use rustls::client::WriteEarlyData; use rustls::{ClientConnection, IoState, Reader, ServerConnection, Writer}; use std::io::{self, Read, Write}; use std::marker::Unpin; use std::pin::Pin; use std::task::{Context, Poll}; pub struct Stream<'a, IO> { pub io: &'a mut IO, pub conn: Conn<'a>, pub eof: bool, } pub(crate) enum Conn<'a> { Client(&'a mut ClientConnection), Server(&'a mut ServerConnection), } impl Conn<'_> { pub(crate) fn is_handshaking(&self) -> bool { match self { Conn::Client(c) => c.is_handshaking(), Conn::Server(c) => c.is_handshaking(), } } pub(crate) fn wants_write(&self) -> bool { match self { Conn::Client(c) => c.wants_write(), Conn::Server(c) => c.wants_write(), } } pub(crate) fn wants_read(&self) -> bool { match self { Conn::Client(c) => c.wants_read(), Conn::Server(c) => c.wants_read(), } } pub(crate) fn write_tls(&mut self, wr: &mut dyn io::Write) -> Result { match self { Conn::Client(c) => c.write_tls(wr), Conn::Server(c) => c.write_tls(wr), } } pub(crate) fn reader(&mut self) -> Reader { match self { Conn::Client(c) => c.reader(), Conn::Server(c) => c.reader(), } } pub(crate) fn writer(&mut self) -> Writer { match self { Conn::Client(c) => c.writer(), Conn::Server(c) => c.writer(), } } pub(crate) fn send_close_notify(&mut self) { match self { Conn::Client(c) => c.send_close_notify(), Conn::Server(c) => c.send_close_notify(), } } pub(crate) fn read_tls(&mut self, rd: &mut dyn io::Read) -> Result { match self { Conn::Client(c) => c.read_tls(rd), Conn::Server(c) => c.read_tls(rd), } } pub(crate) fn process_new_packets(&mut self) -> Result { match self { Conn::Client(c) => c.process_new_packets(), Conn::Server(c) => c.process_new_packets(), } } #[cfg(feature = "early-data")] pub(crate) fn is_early_data_accepted(&self) -> bool { match self { Conn::Client(c) => c.is_early_data_accepted(), Conn::Server(_) => false, } } #[cfg(feature = "early-data")] pub(crate) fn client_early_data(&mut self) -> Option> { match self { Conn::Client(c) => c.early_data(), Conn::Server(_) => None, } } } impl<'a> From<&'a mut ClientConnection> for Conn<'a> { fn from(conn: &'a mut ClientConnection) -> Self { Conn::Client(conn) } } impl<'a> From<&'a mut ServerConnection> for Conn<'a> { fn from(conn: &'a mut ServerConnection) -> Self { Conn::Server(conn) } } trait WriteTls { fn write_tls(&mut self, cx: &mut Context) -> io::Result; } #[derive(Clone, Copy)] enum Focus { Empty, Readable, Writable, } impl<'a, IO: AsyncRead + AsyncWrite + Unpin> Stream<'a, IO> { pub fn new(io: &'a mut IO, conn: impl Into>) -> Self { Stream { io, conn: conn.into(), // The state so far is only used to detect EOF, so either Stream // or EarlyData state should both be all right. eof: false, } } pub fn set_eof(mut self, eof: bool) -> Self { self.eof = eof; self } pub fn as_mut_pin(&mut self) -> Pin<&mut Self> { Pin::new(self) } pub fn complete_io(&mut self, cx: &mut Context) -> Poll> { self.complete_inner_io(cx, Focus::Empty) } fn complete_read_io(&mut self, cx: &mut Context) -> Poll> { struct Reader<'a, 'b, T> { io: &'a mut T, cx: &'a mut Context<'b>, } impl<'a, 'b, T: AsyncRead + Unpin> Read for Reader<'a, 'b, T> { fn read(&mut self, buf: &mut [u8]) -> io::Result { match Pin::new(&mut self.io).poll_read(self.cx, buf) { Poll::Ready(result) => result, Poll::Pending => Err(io::ErrorKind::WouldBlock.into()), } } } let mut reader = Reader { io: self.io, cx }; let n = match self.conn.read_tls(&mut reader) { Ok(n) => n, Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => return Poll::Pending, Err(err) => return Poll::Ready(Err(err)), }; self.conn.process_new_packets().map_err(|err| { // In case we have an alert to send describing this error, // try a last-gasp write -- but don't predate the primary // error. let _ = self.write_tls(cx); io::Error::new(io::ErrorKind::InvalidData, err) })?; Poll::Ready(Ok(n)) } fn complete_write_io(&mut self, cx: &mut Context) -> Poll> { match self.write_tls(cx) { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Poll::Pending, result => Poll::Ready(result), } } fn complete_inner_io( &mut self, cx: &mut Context, focus: Focus, ) -> Poll> { let mut wrlen = 0; let mut rdlen = 0; loop { let mut write_would_block = false; let mut read_would_block = false; while self.conn.wants_write() { match self.complete_write_io(cx) { Poll::Ready(Ok(n)) => wrlen += n, Poll::Pending => { write_would_block = true; break; } Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), } } if !self.eof && self.conn.wants_read() { match self.complete_read_io(cx) { Poll::Ready(Ok(0)) => self.eof = true, Poll::Ready(Ok(n)) => rdlen += n, Poll::Pending => read_would_block = true, Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), } } let would_block = match focus { Focus::Empty => write_would_block || read_would_block, Focus::Readable => read_would_block, Focus::Writable => write_would_block, }; match (self.eof, self.conn.is_handshaking(), would_block) { (true, true, _) => { let err = io::Error::new(io::ErrorKind::UnexpectedEof, "tls handshake eof"); return Poll::Ready(Err(err)); } (_, false, true) => { let would_block = match focus { Focus::Empty => rdlen == 0 && wrlen == 0, Focus::Readable => rdlen == 0, Focus::Writable => wrlen == 0, }; return if would_block { Poll::Pending } else { Poll::Ready(Ok((rdlen, wrlen))) }; } (_, false, _) => return Poll::Ready(Ok((rdlen, wrlen))), (_, true, true) => return Poll::Pending, (..) => (), } } } } impl<'a, IO: AsyncRead + AsyncWrite + Unpin> WriteTls for Stream<'a, IO> { fn write_tls(&mut self, cx: &mut Context) -> io::Result { // TODO writev struct Writer<'a, 'b, T> { io: &'a mut T, cx: &'a mut Context<'b>, } impl<'a, 'b, T: AsyncWrite + Unpin> Write for Writer<'a, 'b, T> { fn write(&mut self, buf: &[u8]) -> io::Result { match Pin::new(&mut self.io).poll_write(self.cx, buf) { Poll::Ready(result) => result, Poll::Pending => Err(io::ErrorKind::WouldBlock.into()), } } fn flush(&mut self) -> io::Result<()> { match Pin::new(&mut self.io).poll_flush(self.cx) { Poll::Ready(result) => result, Poll::Pending => Err(io::ErrorKind::WouldBlock.into()), } } } let mut writer = Writer { io: self.io, cx }; self.conn.write_tls(&mut writer) } } impl<'a, IO: AsyncRead + AsyncWrite + Unpin> AsyncRead for Stream<'a, IO> { fn poll_read( self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8], ) -> Poll> { let this = self.get_mut(); while !this.eof && this.conn.wants_read() { match this.complete_inner_io(cx, Focus::Readable) { Poll::Ready(Ok((0, _))) => break, Poll::Ready(Ok(_)) => (), Poll::Pending => return Poll::Pending, Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), } } let mut reader = this.conn.reader(); match reader.read(buf) { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Poll::Pending, Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => { this.eof = true; Poll::Ready(Err(err)) } result => Poll::Ready(result), } } } impl<'a, IO: AsyncRead + AsyncWrite + Unpin> AsyncWrite for Stream<'a, IO> { fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { let this = self.get_mut(); let len = match this.conn.writer().write(buf) { Ok(n) => n, Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => return Poll::Pending, Err(err) => return Poll::Ready(Err(err)), }; while this.conn.wants_write() { match this.complete_inner_io(cx, Focus::Writable) { Poll::Ready(Ok(_)) => (), Poll::Pending if len != 0 => break, Poll::Pending => return Poll::Pending, Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), } } if len != 0 || buf.is_empty() { Poll::Ready(Ok(len)) } else { // not write zero match this.conn.writer().write(buf) { Ok(0) => Poll::Pending, Ok(n) => Poll::Ready(Ok(n)), Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Poll::Pending, Err(err) => Poll::Ready(Err(err)), } } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let this = self.get_mut(); this.conn.writer().flush()?; while this.conn.wants_write() { ready!(this.complete_inner_io(cx, Focus::Writable))?; } Pin::new(&mut this.io).poll_flush(cx) } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); while this.conn.wants_write() { ready!(this.complete_inner_io(cx, Focus::Writable))?; } Pin::new(&mut this.io).poll_close(cx) } } #[cfg(all(test, feature = "client"))] #[path = "test_stream.rs"] mod test_stream; async-tls-0.13.0/src/rusttls/test_stream.rs000064400000000000000000000171271046102023000170410ustar 00000000000000use super::Stream; use futures_executor::block_on; use futures_io::{AsyncRead, AsyncWrite}; use futures_util::io::{AsyncReadExt, AsyncWriteExt}; use futures_util::task::{noop_waker_ref, Context}; use futures_util::{future, ready}; use rustls::{ Certificate, ClientConfig, ClientConnection, ConnectionCommon, PrivateKey, RootCertStore, ServerConfig, ServerConnection, ServerName, }; use rustls_pemfile::{certs, pkcs8_private_keys}; use std::convert::TryFrom; use std::io::{self, BufReader, Cursor, Read, Write}; use std::pin::Pin; use std::sync::Arc; use std::task::Poll; struct Good<'a, D>(&'a mut ConnectionCommon); impl<'a, D> AsyncRead for Good<'a, D> { fn poll_read( mut self: Pin<&mut Self>, _cx: &mut Context<'_>, mut buf: &mut [u8], ) -> Poll> { Poll::Ready(self.0.write_tls(buf.by_ref())) } } impl<'a, D> AsyncWrite for Good<'a, D> { fn poll_write( mut self: Pin<&mut Self>, _cx: &mut Context<'_>, mut buf: &[u8], ) -> Poll> { let len = self.0.read_tls(buf.by_ref())?; self.0 .process_new_packets() .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; Poll::Ready(Ok(len)) } fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { self.0 .process_new_packets() .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; Poll::Ready(Ok(())) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.0.send_close_notify(); self.poll_flush(cx) } } struct Bad(bool); impl AsyncRead for Bad { fn poll_read( self: Pin<&mut Self>, _cx: &mut Context<'_>, _: &mut [u8], ) -> Poll> { Poll::Ready(Ok(0)) } } impl AsyncWrite for Bad { fn poll_write( self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { if self.0 { Poll::Pending } else { Poll::Ready(Ok(buf.len())) } } fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } } #[test] fn stream_good() -> io::Result<()> { block_on(_stream_good()) } async fn _stream_good() -> io::Result<()> { const FILE: &[u8] = include_bytes!("../../README.md"); let (mut server, mut client) = make_pair(); future::poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?; io::copy(&mut Cursor::new(FILE), &mut server.writer())?; server.send_close_notify(); { let mut good = Good(&mut server); let mut stream = Stream::new(&mut good, &mut client); let mut buf = Vec::new(); stream.read_to_end(&mut buf).await?; assert_eq!(buf, FILE); stream.write_all(b"Hello World!").await?; stream.conn.send_close_notify(); stream.close().await?; } let mut buf = String::new(); server.reader().read_to_string(&mut buf)?; assert_eq!(buf, "Hello World!"); Ok(()) as io::Result<()> } #[test] fn stream_bad() -> io::Result<()> { let fut = async { let (mut server, mut client) = make_pair(); future::poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?; client.set_buffer_limit(Some(1024)); let mut bad = Bad(true); let mut stream = Stream::new(&mut bad, &mut client); assert_eq!( future::poll_fn(|cx| stream.as_mut_pin().poll_write(cx, &[0x42; 8])).await?, 8 ); assert_eq!( future::poll_fn(|cx| stream.as_mut_pin().poll_write(cx, &[0x42; 8])).await?, 8 ); let r = future::poll_fn(|cx| stream.as_mut_pin().poll_write(cx, &[0x00; 1024])).await?; // fill buffer assert!(r < 1024); let mut cx = Context::from_waker(noop_waker_ref()); assert!(stream .as_mut_pin() .poll_write(&mut cx, &[0x01]) .is_pending()); Ok(()) as io::Result<()> }; block_on(fut) } #[test] fn stream_handshake() -> io::Result<()> { let fut = async { let (mut server, mut client) = make_pair(); { let mut good = Good(&mut server); let mut stream = Stream::new(&mut good, &mut client); let (r, w) = future::poll_fn(|cx| stream.complete_io(cx)).await?; assert!(r > 0); assert!(w > 0); future::poll_fn(|cx| stream.complete_io(cx)).await?; // finish server handshake } assert!(!server.is_handshaking()); assert!(!client.is_handshaking()); Ok(()) as io::Result<()> }; block_on(fut) } #[test] fn stream_handshake_eof() -> io::Result<()> { let fut = async { let (_, mut client) = make_pair(); let mut bad = Bad(false); let mut stream = Stream::new(&mut bad, &mut client); let mut cx = Context::from_waker(noop_waker_ref()); let r = stream.complete_io(&mut cx); assert_eq!( r.map_err(|err| err.kind()), Poll::Ready(Err(io::ErrorKind::UnexpectedEof)) ); Ok(()) as io::Result<()> }; block_on(fut) } #[test] fn stream_eof() -> io::Result<()> { let fut = async { let (mut server, mut client) = make_pair(); future::poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?; let mut eof_stream = Bad(false); let mut stream = Stream::new(&mut eof_stream, &mut client); let mut buf = Vec::new(); let res = stream.read_to_end(&mut buf).await; assert_eq!( res.err().map(|e| e.kind()), Some(std::io::ErrorKind::UnexpectedEof) ); Ok(()) as io::Result<()> }; block_on(fut) } fn make_pair() -> (ServerConnection, ClientConnection) { const CERT: &str = include_str!("../../tests/end.cert"); const CHAIN: &str = include_str!("../../tests/end.chain"); const RSA: &str = include_str!("../../tests/end.rsa"); let cert = certs(&mut BufReader::new(Cursor::new(CERT))).unwrap(); let cert = cert.into_iter().map(Certificate).collect(); let mut keys = pkcs8_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap(); let key = PrivateKey(keys.pop().unwrap()); let sconfig = ServerConfig::builder() .with_safe_defaults() .with_no_client_auth() .with_single_cert(cert, key) .unwrap(); let server = ServerConnection::new(Arc::new(sconfig)); let domain = ServerName::try_from("localhost").unwrap(); let mut root_store = RootCertStore::empty(); let chain = certs(&mut BufReader::new(Cursor::new(CHAIN))).unwrap(); let (added, ignored) = root_store.add_parsable_certificates(&chain); assert!(added >= 1 && ignored == 0); let cconfig = ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_store) .with_no_client_auth(); let client = ClientConnection::new(Arc::new(cconfig), domain); (server.unwrap(), client.unwrap()) } fn do_handshake( client: &mut ClientConnection, server: &mut ServerConnection, cx: &mut Context<'_>, ) -> Poll> { let mut good = Good(server); let mut stream = Stream::new(&mut good, client); if stream.conn.is_handshaking() { ready!(stream.complete_io(cx))?; } if stream.conn.wants_write() { ready!(stream.complete_io(cx))?; } Poll::Ready(Ok(())) } async-tls-0.13.0/src/server.rs000064400000000000000000000101351046102023000142650ustar 00000000000000//! The server end of a TLS connection. use crate::common::tls_state::TlsState; use crate::rusttls::stream::Stream; use futures_core::ready; use futures_io::{AsyncRead, AsyncWrite}; use rustls::ServerConnection; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::{io, mem}; /// The server end of a TLS connection. Can be used like any other bidirectional IO stream. /// Wraps the underlying TCP stream. #[derive(Debug)] pub struct TlsStream { pub(crate) io: IO, pub(crate) conn: ServerConnection, pub(crate) state: TlsState, } pub(crate) enum MidHandshake { Handshaking(TlsStream), End, } impl Future for MidHandshake where IO: AsyncRead + AsyncWrite + Unpin, { type Output = io::Result>; #[inline] fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); if let MidHandshake::Handshaking(stream) = this { let eof = !stream.state.readable(); let (io, session) = (&mut stream.io, &mut stream.conn); let mut stream = Stream::new(io, session).set_eof(eof); if stream.conn.is_handshaking() { ready!(stream.complete_io(cx))?; } if stream.conn.wants_write() { ready!(stream.complete_io(cx))?; } } match mem::replace(this, MidHandshake::End) { MidHandshake::Handshaking(stream) => Poll::Ready(Ok(stream)), MidHandshake::End => panic!(), } } } impl AsyncRead for TlsStream where IO: AsyncRead + AsyncWrite + Unpin, { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.conn).set_eof(!this.state.readable()); match this.state { TlsState::Stream | TlsState::WriteShutdown => { match stream.as_mut_pin().poll_read(cx, buf) { Poll::Ready(Ok(0)) => { this.state.shutdown_read(); Poll::Ready(Ok(0)) } Poll::Ready(Ok(n)) => Poll::Ready(Ok(n)), Poll::Ready(Err(ref err)) if err.kind() == io::ErrorKind::ConnectionAborted => { this.state.shutdown_read(); if this.state.writeable() { stream.conn.send_close_notify(); this.state.shutdown_write(); } Poll::Ready(Ok(0)) } Poll::Ready(Err(e)) => Poll::Ready(Err(e)), Poll::Pending => Poll::Pending, } } TlsState::ReadShutdown | TlsState::FullyShutdown => Poll::Ready(Ok(0)), #[cfg(feature = "early-data")] s => unreachable!("server TLS can not hit this state: {:?}", s), } } } impl AsyncWrite for TlsStream where IO: AsyncRead + AsyncWrite + Unpin, { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.conn).set_eof(!this.state.readable()); stream.as_mut_pin().poll_write(cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.conn).set_eof(!this.state.readable()); stream.as_mut_pin().poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.state.writeable() { self.conn.send_close_notify(); self.state.shutdown_write(); } let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.conn).set_eof(!this.state.readable()); stream.as_mut_pin().poll_close(cx) } } async-tls-0.13.0/src/test_0rtt.rs000064400000000000000000000034601046102023000147120ustar 00000000000000use crate::{client::TlsStream, TlsConnector}; use async_std::net::TcpStream; use async_std::sync::Arc; use futures_executor::block_on; use futures_util::io::{AsyncReadExt, AsyncWriteExt}; use rustls::{ClientConfig, OwnedTrustAnchor, RootCertStore}; use std::io; use std::net::ToSocketAddrs; async fn get( config: Arc, domain: &str, rtt0: bool, ) -> io::Result<(TlsStream, String)> { let connector = TlsConnector::from(config).early_data(rtt0); let input = format!("GET / HTTP/1.0\r\nHost: {}\r\n\r\n", domain); let addr = (domain, 443).to_socket_addrs()?.next().unwrap(); let mut buf = Vec::new(); let stream = TcpStream::connect(&addr).await?; let mut stream = connector.connect(domain, stream).await?; stream.write_all(input.as_bytes()).await?; stream.read_to_end(&mut buf).await?; Ok((stream, String::from_utf8(buf).unwrap())) } #[test] fn test_0rtt() { let mut root_certs = RootCertStore::empty(); root_certs.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) })); let mut config = ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_certs) .with_no_client_auth(); config.enable_early_data = true; let config = Arc::new(config); let domain = "mozilla-modern.badssl.com"; let (_, output) = block_on(get(config.clone(), domain, false)).unwrap(); assert!(output.contains("mozilla-modern.badssl.com")); let (io, output) = block_on(get(config.clone(), domain, true)).unwrap(); assert!(output.contains("mozilla-modern.badssl.com")); assert_eq!(io.early_data.0, 0); } async-tls-0.13.0/tests/ca.cert000064400000000000000000000021571046102023000142330ustar 00000000000000-----BEGIN CERTIFICATE----- MIIDGTCCAgGgAwIBAgIUEEBOQwP/6Dvr6vUpsHzSKdvva68wDQYJKoZIhvcNAQEL BQAwHDEaMBgGA1UEAwwRY2EudGVzdHNlcnZlci5jb20wHhcNMjIwNjA4MTAwOTI3 WhcNMzIwNjA1MTAwOTI3WjAcMRowGAYDVQQDDBFjYS50ZXN0c2VydmVyLmNvbTCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALxMc7nyY3HhRWUtmtyxKgPq 5jWlTKaJI4TO5xnYzCHYyDHT2Ouov3hXQxtLlRFvHEhCjLmDdElfaZvedZExxTGA yb/4vHu1Oo0fbFQUXwgWRsdhbZweIpvvMGpeSf8TD3gM33WvJvlm0ytzMi+FcNO+ K/agtfyuakvRnCgUqT7t+mpdApOF0GlMhW7yNurLYQErdITSEHo7B1LpyIxAzdDk 2RDg6Jw+owIqn35GRVR7KHgvmRu//eyPjN0gzTT0iPGX5FB5AE5pbv3coZ5Q3LOO MzTM6bqTpHQWB8B/LYbAI/sgWvq9pGlzqwjD20+mIt/R3pCq3Si1PTCgvVxnCBMC AwEAAaNTMFEwHQYDVR0OBBYEFB0N4W0qDG+IkkvDWR5+Ndx11PtvMB8GA1UdIwQY MBaAFB0N4W0qDG+IkkvDWR5+Ndx11PtvMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI hvcNAQELBQADggEBALe3wec6bKeolaVvh+Y6SZqcM8Dv9cTp4Hkw6oCt0pOsAThr WCgJIwUx8XiCx9HBHiCXLHlsV4mHrbuZHCP7UFRwe4ujnT1hRvr44mu9pgvrT4Ff 483xT9AqUtkkwXdHjdgcy5LzfGaDOF404e4wp26Rcg/ZnHT4Sz5eKhZgM64L30/Q PKy7nvz6iXtEX8+zHnfRhpC/QPn08t/YGO6hDCCkuc5kDUTMQiLxm+TtDwaw6dyC OH2E1xTBrNAUaE0pMqQ2D2fZu81SKhZ8vjl/UvHsnWwJoix8JZDYs2Oq97DuMfpV IAz5xDMH0GQxNX1E8ScqwoNF3pIkgZ6hvOmK8Zc= -----END CERTIFICATE----- async-tls-0.13.0/tests/ca.rsa000064400000000000000000000032501046102023000140560ustar 00000000000000-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8THO58mNx4UVl LZrcsSoD6uY1pUymiSOEzucZ2Mwh2Mgx09jrqL94V0MbS5URbxxIQoy5g3RJX2mb 3nWRMcUxgMm/+Lx7tTqNH2xUFF8IFkbHYW2cHiKb7zBqXkn/Ew94DN91ryb5ZtMr czIvhXDTviv2oLX8rmpL0ZwoFKk+7fpqXQKThdBpTIVu8jbqy2EBK3SE0hB6OwdS 6ciMQM3Q5NkQ4OicPqMCKp9+RkVUeyh4L5kbv/3sj4zdIM009Ijxl+RQeQBOaW79 3KGeUNyzjjM0zOm6k6R0FgfAfy2GwCP7IFr6vaRpc6sIw9tPpiLf0d6Qqt0otT0w oL1cZwgTAgMBAAECggEAL+fde33k+gifkKXBJAu4zkVZa2WSMj5quHOxTRyglq3i BPdKVW5ZxEKjMpMQql5T2jiAOARvNemd40d+LsH0UutaqMoeHfUWH+hSNbP3F4Yf XMN3UQRDtttsPGuftNMDGP5hbb3xkvsrl37X0kpHUb+szLLHijFPntmFupbDFlyi n6xJcnNmRfeJGtEizXSvhZgQqxMX1jA1sNjZKdVSeU5b/gpKTS5b8LFKMkV55Z7M eRZULzpiYiLj7Thfqg6blsUOnavn7jlAdYrUFhPW1+og6thLVLSuZAE9shlkvJk0 fWHnHpU3H+Z64GSLTNJO0yzTx831EsETA1C1pdSnrQKBgQDXWnK3sRZYCimonIXJ l8H8P/13D1H0fk5THxs9CMhp3mzg+lShrGD7TtUg6kty08NAlnanD2Gr9tSEGhwQ 99odV/VJOb2wBdVsrLXnPjATWaMDTpg8DoS9HCMNHaqva7z4H9qf5ETzz6jSDjfv Q30HabOiu5ve2Sd1gAAz9iUAzwKBgQDf1sMtVyA93FdFd6gLLKUaf/K2i7aV6zwD 7PTgFi7Nlhpx8b2kRchfe8vL5AQX5HP6xL7Qakc/33apyDYLpiwPOfi/4WSAj62p i4eCFSRH3KP8pCyF3CVVfvAW55kRkAgTUemARo/RKBDdKCftl7VwGVVUx66NDQgZ UdJ9lYrtfQKBgQCSN14mbH1AP34zdjq8OmbCb2wX93XsrAlgFIG4+gqhHx0Lz8vA jbq3RrochBfAGhm6864SFbZ6PLfkDZo6xx33yG3JTwQEzKExDT7gh++6Y6TzrNxe AnmLsNv3nsnyGxON4Ire/uz2IWt9W5wMvuTYMKN//SOCtiw9cjAxF5HeLwKBgA3G SJKEtw43fgNfXq75W1urHgOPy3ekVzgin9seYljycMQsTTBLvw3pL1xntrzFqUF4 VAm9UI8ksEe4c2L6IeH5/k3IkAe9T8GMPZWKuYXWlRzZ+YdvSbjeK+Ys8GV9SJHv y3/CV91WKtuBOhvL5zVzyaWC+EHj2Gi+eUaTKk59AoGBAKOqyM1zCKlPt82tg/12 MsQH+83bfvZz1Az0HCfa8Qu4zLVCbpMw/XGZP6GZgqQTY0VJD/Mhk/W9XI/Qb5oP j9MxnSd5HkeMYUyUzOhDq5iV8T7X2+bG3eW5B0JHqqXkotQUyu1G4CKDJQextLQl oK8YBYWxdZbTg1RO1IGeofjr -----END PRIVATE KEY----- async-tls-0.13.0/tests/end.cert000064400000000000000000000023101046102023000144050ustar 00000000000000-----BEGIN CERTIFICATE----- MIIDXDCCAkSgAwIBAgIUdMf6o8a7bRANDFQnmDuYgvN7EbEwDQYJKoZIhvcNAQEL BQAwHDEaMBgGA1UEAwwRY2EudGVzdHNlcnZlci5jb20wHhcNMjIwNjA4MTAwOTI3 WhcNMzIwNjA1MTAwOTI3WjAZMRcwFQYDVQQDDA50ZXN0c2VydmVyLmNvbTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJe4nuHgBT1neEVGMeyWEghVaKup DY1p+1rwHjzBQoqHdGsLuiJWXjAuJ72EfCn+pVzYx7KYW10KS6Y19Dk4DXdvCjuN 4S0Pxh8CJbrELRSVe7RRPjDycopQsxBfkQjiTkNn8IZaSh6ITPrbDWyA0TPxwYDY m2jln0OtRuINwAs8pCBCG2rrF3VvDtBJbO0jWhAK3Iy71zSDd0q7YKClEUj0ub6O me4YutijMIreyZmEI/z4L4Wzk8mtEDIdiiMyRlWLsYns4Hya+EoopCbAjaVR7CnQ KDBL4H+8M2s24QALHI9cEwVZqlDdpQM5A+lLza1C32/KeMHUt/uo8wRE420CAwEA AaOBmDCBlTBTBgNVHREETDBKgg50ZXN0c2VydmVyLmNvbYIVc2Vjb25kLnRlc3Rz ZXJ2ZXIuY29tgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwHQYD VR0OBBYEFAh6MVz1pzt1yPvm85irb0UZcdgQMB8GA1UdIwQYMBaAFB0N4W0qDG+I kkvDWR5+Ndx11PtvMA0GCSqGSIb3DQEBCwUAA4IBAQBjPBb6rEshV9ZTstzNvH/K nWpQvaivORxUfK0WwdaC/JLnkeu9pge0bAOiqbtsUhWPcqpLVxVO3Xs+IlAeZXqX CHHtDI/8ygzMJ/me96tKaPktJI310MQiYOLJgPoOEANpVWAfpo+pPBQPKhLvzbw5 gIK5YqV5slIg86uQJnAasvqJau2L3ex4tUy4DN/mJhdaRYD40D7IwV1RcNcbvSwh e3TL5WnHfqjoAeuxJA5+emEaXpNe54K7ymyMweo1gZaNZiYVmLRTBuQak2WUudnJ mSuv299j7AgRtsKqkzNyOI+g4OQfACtG1Zfi57Z+pTZKB0FcwZNDXIDhwBmJ3oIS -----END CERTIFICATE----- async-tls-0.13.0/tests/end.chain000064400000000000000000000044671046102023000145510ustar 00000000000000-----BEGIN CERTIFICATE----- MIIDXDCCAkSgAwIBAgIUdMf6o8a7bRANDFQnmDuYgvN7EbEwDQYJKoZIhvcNAQEL BQAwHDEaMBgGA1UEAwwRY2EudGVzdHNlcnZlci5jb20wHhcNMjIwNjA4MTAwOTI3 WhcNMzIwNjA1MTAwOTI3WjAZMRcwFQYDVQQDDA50ZXN0c2VydmVyLmNvbTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJe4nuHgBT1neEVGMeyWEghVaKup DY1p+1rwHjzBQoqHdGsLuiJWXjAuJ72EfCn+pVzYx7KYW10KS6Y19Dk4DXdvCjuN 4S0Pxh8CJbrELRSVe7RRPjDycopQsxBfkQjiTkNn8IZaSh6ITPrbDWyA0TPxwYDY m2jln0OtRuINwAs8pCBCG2rrF3VvDtBJbO0jWhAK3Iy71zSDd0q7YKClEUj0ub6O me4YutijMIreyZmEI/z4L4Wzk8mtEDIdiiMyRlWLsYns4Hya+EoopCbAjaVR7CnQ KDBL4H+8M2s24QALHI9cEwVZqlDdpQM5A+lLza1C32/KeMHUt/uo8wRE420CAwEA AaOBmDCBlTBTBgNVHREETDBKgg50ZXN0c2VydmVyLmNvbYIVc2Vjb25kLnRlc3Rz ZXJ2ZXIuY29tgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwHQYD VR0OBBYEFAh6MVz1pzt1yPvm85irb0UZcdgQMB8GA1UdIwQYMBaAFB0N4W0qDG+I kkvDWR5+Ndx11PtvMA0GCSqGSIb3DQEBCwUAA4IBAQBjPBb6rEshV9ZTstzNvH/K nWpQvaivORxUfK0WwdaC/JLnkeu9pge0bAOiqbtsUhWPcqpLVxVO3Xs+IlAeZXqX CHHtDI/8ygzMJ/me96tKaPktJI310MQiYOLJgPoOEANpVWAfpo+pPBQPKhLvzbw5 gIK5YqV5slIg86uQJnAasvqJau2L3ex4tUy4DN/mJhdaRYD40D7IwV1RcNcbvSwh e3TL5WnHfqjoAeuxJA5+emEaXpNe54K7ymyMweo1gZaNZiYVmLRTBuQak2WUudnJ mSuv299j7AgRtsKqkzNyOI+g4OQfACtG1Zfi57Z+pTZKB0FcwZNDXIDhwBmJ3oIS -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDGTCCAgGgAwIBAgIUEEBOQwP/6Dvr6vUpsHzSKdvva68wDQYJKoZIhvcNAQEL BQAwHDEaMBgGA1UEAwwRY2EudGVzdHNlcnZlci5jb20wHhcNMjIwNjA4MTAwOTI3 WhcNMzIwNjA1MTAwOTI3WjAcMRowGAYDVQQDDBFjYS50ZXN0c2VydmVyLmNvbTCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALxMc7nyY3HhRWUtmtyxKgPq 5jWlTKaJI4TO5xnYzCHYyDHT2Ouov3hXQxtLlRFvHEhCjLmDdElfaZvedZExxTGA yb/4vHu1Oo0fbFQUXwgWRsdhbZweIpvvMGpeSf8TD3gM33WvJvlm0ytzMi+FcNO+ K/agtfyuakvRnCgUqT7t+mpdApOF0GlMhW7yNurLYQErdITSEHo7B1LpyIxAzdDk 2RDg6Jw+owIqn35GRVR7KHgvmRu//eyPjN0gzTT0iPGX5FB5AE5pbv3coZ5Q3LOO MzTM6bqTpHQWB8B/LYbAI/sgWvq9pGlzqwjD20+mIt/R3pCq3Si1PTCgvVxnCBMC AwEAAaNTMFEwHQYDVR0OBBYEFB0N4W0qDG+IkkvDWR5+Ndx11PtvMB8GA1UdIwQY MBaAFB0N4W0qDG+IkkvDWR5+Ndx11PtvMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI hvcNAQELBQADggEBALe3wec6bKeolaVvh+Y6SZqcM8Dv9cTp4Hkw6oCt0pOsAThr WCgJIwUx8XiCx9HBHiCXLHlsV4mHrbuZHCP7UFRwe4ujnT1hRvr44mu9pgvrT4Ff 483xT9AqUtkkwXdHjdgcy5LzfGaDOF404e4wp26Rcg/ZnHT4Sz5eKhZgM64L30/Q PKy7nvz6iXtEX8+zHnfRhpC/QPn08t/YGO6hDCCkuc5kDUTMQiLxm+TtDwaw6dyC OH2E1xTBrNAUaE0pMqQ2D2fZu81SKhZ8vjl/UvHsnWwJoix8JZDYs2Oq97DuMfpV IAz5xDMH0GQxNX1E8ScqwoNF3pIkgZ6hvOmK8Zc= -----END CERTIFICATE----- async-tls-0.13.0/tests/end.rsa000064400000000000000000000032501046102023000142410ustar 00000000000000-----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCXuJ7h4AU9Z3hF RjHslhIIVWirqQ2Nafta8B48wUKKh3RrC7oiVl4wLie9hHwp/qVc2MeymFtdCkum NfQ5OA13bwo7jeEtD8YfAiW6xC0UlXu0UT4w8nKKULMQX5EI4k5DZ/CGWkoeiEz6 2w1sgNEz8cGA2Jto5Z9DrUbiDcALPKQgQhtq6xd1bw7QSWztI1oQCtyMu9c0g3dK u2CgpRFI9Lm+jpnuGLrYozCK3smZhCP8+C+Fs5PJrRAyHYojMkZVi7GJ7OB8mvhK KKQmwI2lUewp0CgwS+B/vDNrNuEACxyPXBMFWapQ3aUDOQPpS82tQt9vynjB1Lf7 qPMERONtAgMBAAECggEAE45tEGFfW4hcRNWk9sFrRp63tLTANfOsvg6IXz1r9c46 79ddoTJvxpcUcUXD+WfE6eBZcUhZMndyw9RdjAiRF818zKtflH7dgz60HUxPIUGZ Zec49Hcz02dOhXREDhiZjO+3XaICvsfD4Gve4ZcDIEZvskt5UL6UVlVd9yJdmDUA dM8w+0CiWVDuQW6LIrLERPIbhWo+G6KMgi+wsL6nFTEpZu3qK7G+m2gcXWAc9VZ5 nvPJE/imrWvOuCbCoHAj5CYBvtx7ZQwNGmTDwH1hupoAPirlnidIxT2uJJ8Vy7cT FoqF0Kksxg3L5gE6/pzbOqTMqdIS1h4UVCF8bvuyIwKBgQDPko3VW6vOEv93JOHA lT8Vbe2G6Ix8S5IEuAOHogD7Tvt4ghlAVdZsl0iG4MHM1md/arADjo1EYSMwcqpg hma0+9Y7s4rpiChxqZQVJ29K7Xp57gXAwbma4t7DGqYmMYrZFPT0GGC4QDrVWI93 yqSTlhJnBwdnwk0n0r68rZaUNwKBgQC7HkyXkOsFYcqBN8SSE62wqdx30xLeLdI+ kBsdZQfTAevXk1zvuLfRk6AJlGaG7F+5TVdeYochLRibAKqcqKLjg2GMqTTZIlC7 QNYSvWZ/Z2+Yp6vsg5c6pELIFV+EZn+xmjRRsiLMa2Gz5fcFim/VoU1+OV+KZLws hr6GodU7ewKBgA92aV5lb8zwGVu0waRo+cQM5k7Qb/aqYnw7gPfPl6cg4Ra/CkaC nnCEbICvqYAq0JbrSaVaLyfS3J41TH/YQzpkMDdOTqDK5chhy4gv4diBDEic9IzB YaQqFlIOYCYkNqWpK/4q+rl+/2L5L+bKj6v21/QYz/JoxPqcdlzzyW3NAoGADuyE uVXymLRK/XFgOTJemQeDMP9hsty+twSxVO/Y5uhxUflL4Ua/SnTWv8zZPIufutzD SiDbnDbHjp1H/kSo8TJqVlisgWDuRXEPYeE/SowKZ4d/+9Ym+qNdPC4QNzQhnR2q bJWjluA9o6aExCldcBF0Z8vVpekQ5RA+I+jGY/8CgYB2j36c21Z4hsyupUNBwCDB 0tkqMschuK8itsUc2t9yjlt8LDSVko35jFwOMRPR+pGWYQT0aZC13FWr7eEEAQ70 Us62j6lCUZkx70ELRYzkOhj3eFQyHOh7Q/S88tZWuCadXk5/HsUYA40G0uvC3y+Y y4L361HmTm9TdNKq2KyN2w== -----END PRIVATE KEY----- async-tls-0.13.0/tests/gen_cert_key.bash000075500000000000000000000014401046102023000162630ustar 00000000000000#!/bin/bash set -ex DIR=${1-$(pwd)} CACERT="${DIR}/ca.cert" CAKEY="${DIR}/ca.rsa" KEY="${DIR}/end.rsa" CERT="${DIR}/end.cert" CHAIN="${DIR}/end.chain" # cleanup if [ -f "$CERT" ]; then rm -f "$CERT"; fi if [ -f "$KEY" ]; then rm -f "$KEY"; fi if [ -f "$CACERT"]; then rm -f "$CACERT"; fi if [ -f "$CAKEY"]; then rm -f "$CAKEY"; fi if [ -f "$CHAIN"]; then rm -f "$CHAIN"; fi # generate ca openssl req -x509 -newkey rsa:2048 -days 3650 -keyout "$CAKEY" -out "$CACERT" -nodes -subj /CN=ca.testserver.com # generate certs openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -out "$CERT" -keyout "$KEY" -keyform P12 -subj /CN=testserver.com -config "${DIR}/openssl.cfg" -CA "$CACERT" -CAkey "$CAKEY" # make key accessible #yolo chmod 664 "$KEY" # concat chain cat "$CERT" "$CACERT" > "$CHAIN"async-tls-0.13.0/tests/openssl.cfg000064400000000000000000000003331046102023000151270ustar 00000000000000[req] distinguished_name=dn x509_extensions=ext [ dn ] CN=testserver.com [ ext ] subjectAltName = @alt_names [alt_names] DNS.1 = testserver.com DNS.2 = second.testserver.com DNS.3 = localhost IP.1 = 127.0.0.1 IP.2 = ::1async-tls-0.13.0/tests/test.rs000064400000000000000000000073401046102023000143150ustar 00000000000000use async_std::channel::bounded; use async_std::io; use async_std::net::{TcpListener, TcpStream}; use async_std::prelude::*; use async_std::task; use async_tls::{TlsAcceptor, TlsConnector}; use lazy_static::lazy_static; use rustls::{Certificate, ClientConfig, PrivateKey, RootCertStore, ServerConfig}; use rustls_pemfile::{certs, pkcs8_private_keys}; use std::io::{BufReader, Cursor}; use std::net::SocketAddr; use std::sync::Arc; const CERT: &str = include_str!("end.cert"); const CHAIN: &str = include_str!("end.chain"); const RSA: &str = include_str!("end.rsa"); lazy_static! { static ref TEST_SERVER: (SocketAddr, &'static str, Vec>) = { let cert = certs(&mut BufReader::new(Cursor::new(CERT))).unwrap(); let cert = cert.into_iter().map(Certificate).collect(); let chain = certs(&mut BufReader::new(Cursor::new(CHAIN))).unwrap(); let mut keys = pkcs8_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap(); let key = PrivateKey(keys.pop().unwrap()); let sconfig = ServerConfig::builder() .with_safe_defaults() .with_no_client_auth() .with_single_cert(cert, key) .unwrap(); let acceptor = TlsAcceptor::from(Arc::new(sconfig)); let (send, recv) = bounded(1); task::spawn(async move { let addr = SocketAddr::from(([127, 0, 0, 1], 0)); let listener = TcpListener::bind(&addr).await?; send.send(listener.local_addr()?).await.unwrap(); let mut incoming = listener.incoming(); while let Some(stream) = incoming.next().await { let acceptor = acceptor.clone(); task::spawn(async move { use futures_util::io::AsyncReadExt; let stream = acceptor.accept(stream?).await?; let (mut reader, mut writer) = stream.split(); io::copy(&mut reader, &mut writer).await?; Ok(()) as io::Result<()> }); } Ok(()) as io::Result<()> }); let addr = task::block_on(async move { recv.recv().await.unwrap() }); (addr, "localhost", chain) }; } fn start_server() -> &'static (SocketAddr, &'static str, Vec>) { &*TEST_SERVER } async fn start_client(addr: SocketAddr, domain: &str, config: Arc) -> io::Result<()> { const FILE: &[u8] = include_bytes!("../README.md"); let config = TlsConnector::from(config); let mut buf = vec![0; FILE.len()]; let stream = TcpStream::connect(&addr).await?; let mut stream = config.connect(domain, stream).await?; stream.write_all(FILE).await?; stream.read_exact(&mut buf).await?; assert_eq!(buf, FILE); stream.flush().await?; Ok(()) } #[test] fn pass() { let (addr, domain, chain) = start_server(); let mut root_store = RootCertStore::empty(); let (added, ignored) = root_store.add_parsable_certificates(&chain); assert!(added >= 1 && ignored == 0); let config = ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_store) .with_no_client_auth(); task::block_on(start_client(*addr, domain, Arc::new(config))).unwrap(); } #[test] fn fail() { let (addr, domain, chain) = start_server(); let mut root_store = RootCertStore::empty(); let (added, ignored) = root_store.add_parsable_certificates(&chain); assert!(added >= 1 && ignored == 0); let config = ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_store) .with_no_client_auth(); let config = Arc::new(config); assert_ne!(domain, &"google.com"); assert!(task::block_on(start_client(*addr, "google.com", config)).is_err()); }