From 55f643c2e2cc08a26a7c2cb61a7cefdfa5e5082d Mon Sep 17 00:00:00 2001 From: Matthias Wahl Date: Wed, 1 Feb 2023 22:01:36 +0100 Subject: [PATCH] feat: port to rustls 0.20 * attempt to port to rustls 0.20 * clippy * format * Fix test certificates (expired) and add a script to regenerate them. * Fix hanging and failing unit-tests UnexpectedEof errors are bubbled up from rustls. Tests needed to changed slightly, but are en par with tokio/tls. * Fix integration tests * Fix client and server examples * Update async-std version used for testing --------- Co-authored-by: Jason Mobarak --- Cargo.toml | 16 +++-- examples/client/Cargo.toml | 5 +- examples/client/src/main.rs | 19 +++--- examples/server/Cargo.toml | 9 +-- examples/server/src/main.rs | 38 +++++++---- src/acceptor.rs | 18 ++++-- src/client.rs | 19 +++--- src/connector.rs | 37 ++++++++--- src/rusttls/stream.rs | 53 ++++++++------- src/rusttls/test_stream.rs | 124 +++++++++++++++++++++--------------- src/server.rs | 28 ++++---- src/test_0rtt.rs | 19 ++++-- tests/ca.cert | 19 ++++++ tests/ca.rsa | 28 ++++++++ tests/end.cert | 40 ++++++------ tests/end.chain | 120 ++++++++++------------------------ tests/end.rsa | 55 ++++++++-------- tests/gen_cert_key.bash | 28 ++++++++ tests/google.rs | 7 +- tests/openssl.cfg | 13 ++++ tests/test.rs | 65 ++++++++++--------- 21 files changed, 442 insertions(+), 318 deletions(-) create mode 100644 tests/ca.cert create mode 100644 tests/ca.rsa create mode 100755 tests/gen_cert_key.bash create mode 100644 tests/openssl.cfg diff --git a/Cargo.toml b/Cargo.toml index daf7a87..b05fe1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,12 @@ [package] name = "async-tls" version = "0.11.0" -authors = ["The async-rs developers", "Florian Gilcher ", "dignifiedquire ", "quininer kel "] +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" @@ -18,9 +23,10 @@ appveyor = { repository = "async-std/async-tls" } [dependencies] futures-io = "0.3.5" futures-core = "0.3.5" -rustls = "0.19.0" -webpki = { version = "0.21.3", optional = true } -webpki-roots = { version = "0.21.0", optional = true } +rustls = "0.20.6" +rustls-pemfile = "1.0" +webpki = { version = "0.22.0", optional = true } +webpki-roots = { version = "0.22.3", optional = true } [features] default = ["client", "server"] @@ -32,7 +38,7 @@ server = [] lazy_static = "1" futures-executor = "0.3.5" futures-util = { version = "0.3.5", features = ["io"] } -async-std = { version = "1.0", features = ["unstable"] } +async-std = { version = "1.11", features = ["unstable"] } [[test]] name = "test" diff --git a/examples/client/Cargo.toml b/examples/client/Cargo.toml index 03a37c0..0f23637 100644 --- a/examples/client/Cargo.toml +++ b/examples/client/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] structopt = "0.3.9" -rustls = "0.19.0" -async-std = "1.5.0" +rustls = "0.20.6" +rustls-pemfile = "1.0" +async-std = "1.11.0" async-tls = { path = "../.." } diff --git a/examples/client/src/main.rs b/examples/client/src/main.rs index 5e04678..5eedbd6 100644 --- a/examples/client/src/main.rs +++ b/examples/client/src/main.rs @@ -5,8 +5,9 @@ use async_std::task; use async_tls::TlsConnector; use rustls::ClientConfig; +use rustls_pemfile::certs; -use std::io::Cursor; +use std::io::{BufReader, Cursor}; use std::net::ToSocketAddrs; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -81,12 +82,14 @@ fn main() -> io::Result<()> { } async fn connector_for_ca_file(cafile: &Path) -> io::Result { - let mut config = ClientConfig::new(); - let file = async_std::fs::read(cafile).await?; - let mut pem = Cursor::new(file); - config - .root_store - .add_pem_file(&mut pem) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))?; + let mut root_store = rustls::RootCertStore::empty(); + let ca_bytes = async_std::fs::read(cafile).await?; + let cert = certs(&mut BufReader::new(Cursor::new(ca_bytes))).unwrap(); + debug_assert_eq!((1, 0), root_store.add_parsable_certificates(&cert)); + + let config = ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(root_store) + .with_no_client_auth(); Ok(TlsConnector::from(Arc::new(config))) } diff --git a/examples/server/Cargo.toml b/examples/server/Cargo.toml index d7aca4c..f69f39d 100644 --- a/examples/server/Cargo.toml +++ b/examples/server/Cargo.toml @@ -5,8 +5,9 @@ authors = ["The async-rs developers", "quininer "] edition = "2018" [dependencies] -structopt = "0.3.9" -async-std = "1.5.0" +async-std = "1.11.0" async-tls = { path = "../.." } -rustls = "0.19.0" -webpki = "0.21.3" +futures-lite = "1.12.0" +rustls = "0.20.6" +rustls-pemfile = "1.0" +structopt = "0.3.9" diff --git a/examples/server/src/main.rs b/examples/server/src/main.rs index ad6045c..ec426bb 100644 --- a/examples/server/src/main.rs +++ b/examples/server/src/main.rs @@ -1,10 +1,11 @@ use async_std::io; use async_std::net::{TcpListener, TcpStream}; -use async_std::prelude::*; +use async_std::stream::StreamExt; use async_std::task; use async_tls::TlsAcceptor; -use rustls::internal::pemfile::{certs, rsa_private_keys}; -use rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig}; +use futures_lite::io::AsyncWriteExt; +use rustls::{Certificate, PrivateKey, ServerConfig}; +use rustls_pemfile::{certs, read_one, Item}; use std::fs::File; use std::io::BufReader; @@ -28,14 +29,23 @@ struct Options { /// Load the passed certificates file fn load_certs(path: &Path) -> io::Result> { - certs(&mut BufReader::new(File::open(path)?)) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert")) + Ok(certs(&mut BufReader::new(File::open(path)?)) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))? + .into_iter() + .map(Certificate) + .collect()) } /// Load the passed keys file -fn load_keys(path: &Path) -> io::Result> { - rsa_private_keys(&mut BufReader::new(File::open(path)?)) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key")) +fn load_key(path: &Path) -> io::Result { + match read_one(&mut BufReader::new(File::open(path)?)) { + Ok(Some(Item::RSAKey(data) | Item::PKCS8Key(data))) => Ok(PrivateKey(data)), + Ok(_) => Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("invalid key in {}", path.display()), + )), + Err(e) => Err(io::Error::new(io::ErrorKind::InvalidInput, e)), + } } /// Configure the server using rusttls @@ -44,13 +54,15 @@ fn load_keys(path: &Path) -> io::Result> { /// A TLS server needs a certificate and a fitting private key fn load_config(options: &Options) -> io::Result { let certs = load_certs(&options.cert)?; - let mut keys = load_keys(&options.key)?; + debug_assert_eq!(1, certs.len()); + let key = load_key(&options.key)?; // we don't use client authentication - let mut config = ServerConfig::new(NoClientAuth::new()); - config + let config = ServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth() // set this server to use one cert together with the loaded private key - .set_single_cert(certs, keys.remove(0)) + .with_single_cert(certs, key) .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?; Ok(config) @@ -78,7 +90,7 @@ async fn handle_connection(acceptor: &TlsAcceptor, tcp_stream: &mut TcpStream) - ) .await?; - tls_stream.flush().await?; + tls_stream.close().await?; Ok(()) } diff --git a/src/acceptor.rs b/src/acceptor.rs index 2563f8b..3901039 100644 --- a/src/acceptor.rs +++ b/src/acceptor.rs @@ -2,7 +2,7 @@ use crate::common::tls_state::TlsState; use crate::server; use futures_io::{AsyncRead, AsyncWrite}; -use rustls::{ServerConfig, ServerSession}; +use rustls::{ServerConfig, ServerConnection}; use std::future::Future; use std::io; use std::pin::Pin; @@ -39,17 +39,23 @@ impl TlsAcceptor { self.accept_with(stream, |_| ()) } - // Currently private, as exposing ServerSessions exposes rusttls + // Currently private, as exposing ServerConnections exposes rusttls fn accept_with(&self, stream: IO, f: F) -> Accept where IO: AsyncRead + AsyncWrite + Unpin, - F: FnOnce(&mut ServerSession), + F: FnOnce(&mut ServerConnection), { - let mut session = ServerSession::new(&self.inner); - f(&mut session); + 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 { - session, + conn, io: stream, state: TlsState::Stream, })) diff --git a/src/client.rs b/src/client.rs index 890e210..2e610a6 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,20 +4,18 @@ use crate::common::tls_state::TlsState; use crate::rusttls::stream::Stream; use futures_core::ready; use futures_io::{AsyncRead, AsyncWrite}; -use rustls::ClientSession; +use rustls::ClientConnection; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::{io, mem}; -use rustls::Session; - /// 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: ClientSession, + pub(crate) session: ClientConnection, pub(crate) state: TlsState, #[cfg(feature = "early-data")] @@ -58,11 +56,11 @@ where let (io, session) = (&mut stream.io, &mut stream.session); let mut stream = Stream::new(io, session).set_eof(eof); - if stream.session.is_handshaking() { + if stream.conn.is_handshaking() { ready!(stream.complete_io(cx))?; } - if stream.session.wants_write() { + if stream.conn.wants_write() { ready!(stream.complete_io(cx))?; } } @@ -90,17 +88,20 @@ where 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 stream.session.is_handshaking() { + if is_handshaking { ready!(stream.complete_io(cx))?; } // write early data (fallback) - if !stream.session.is_early_data_accepted() { + if !is_early_data_accepted { while *pos < data.len() { let len = ready!(stream.as_mut_pin().poll_write(cx, &data[*pos..]))?; *pos += len; @@ -127,7 +128,7 @@ where Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::ConnectionAborted => { this.state.shutdown_read(); if this.state.writeable() { - stream.session.send_close_notify(); + stream.conn.send_close_notify(); this.state.shutdown_write(); } Poll::Ready(Ok(0)) diff --git a/src/connector.rs b/src/connector.rs index 594be81..e686a42 100644 --- a/src/connector.rs +++ b/src/connector.rs @@ -3,13 +3,13 @@ use crate::common::tls_state::TlsState; use crate::client; use futures_io::{AsyncRead, AsyncWrite}; -use rustls::{ClientConfig, ClientSession}; +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}; -use webpki::DNSNameRef; /// The TLS connecting part. The acceptor drives /// the client side of the TLS handshake process. It works @@ -64,10 +64,18 @@ impl From for TlsConnector { impl Default for TlsConnector { fn default() -> Self { - let mut config = ClientConfig::new(); - config - .root_store - .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); + let mut root_certs = RootCertStore::empty(); + root_certs.add_server_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() } } @@ -102,14 +110,14 @@ impl TlsConnector { self.connect_with(domain, stream, |_| ()) } - // NOTE: Currently private, exposing ClientSession exposes rusttls + // 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 ClientSession), + F: FnOnce(&mut ClientConnection), { - let domain = match DNSNameRef::try_from_ascii_str(domain.as_ref()) { + let domain = match ServerName::try_from(domain.as_ref()) { Ok(domain) => domain, Err(_) => { return Connect(ConnectInner::Error(Some(io::Error::new( @@ -119,7 +127,16 @@ impl TlsConnector { } }; - let mut session = ClientSession::new(&self.inner, 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"))] diff --git a/src/rusttls/stream.rs b/src/rusttls/stream.rs index 3b80a20..4bb544c 100644 --- a/src/rusttls/stream.rs +++ b/src/rusttls/stream.rs @@ -1,18 +1,18 @@ use futures_core::ready; use futures_io::{AsyncRead, AsyncWrite}; -use rustls::Session; +use rustls::ConnectionCommon; use std::io::{self, Read, Write}; use std::marker::Unpin; use std::pin::Pin; use std::task::{Context, Poll}; -pub struct Stream<'a, IO, S> { +pub struct Stream<'a, IO, D> { pub io: &'a mut IO, - pub session: &'a mut S, + pub conn: &'a mut ConnectionCommon, pub eof: bool, } -trait WriteTls { +trait WriteTls { fn write_tls(&mut self, cx: &mut Context) -> io::Result; } @@ -23,11 +23,11 @@ enum Focus { Writable, } -impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> { - pub fn new(io: &'a mut IO, session: &'a mut S) -> Self { +impl<'a, IO: AsyncRead + AsyncWrite + Unpin, D: Unpin> Stream<'a, IO, D> { + pub fn new(io: &'a mut IO, conn: &'a mut ConnectionCommon) -> Self { Stream { io, - session, + conn, // The state so far is only used to detect EOF, so either Stream // or EarlyData state should both be all right. eof: false, @@ -64,13 +64,13 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> { let mut reader = Reader { io: self.io, cx }; - let n = match self.session.read_tls(&mut reader) { + 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.session.process_new_packets().map_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. @@ -101,7 +101,7 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> { let mut write_would_block = false; let mut read_would_block = false; - while self.session.wants_write() { + while self.conn.wants_write() { match self.complete_write_io(cx) { Poll::Ready(Ok(n)) => wrlen += n, Poll::Pending => { @@ -112,7 +112,7 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> { } } - if !self.eof && self.session.wants_read() { + 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, @@ -127,7 +127,7 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> { Focus::Writable => write_would_block, }; - match (self.eof, self.session.is_handshaking(), 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)); @@ -153,7 +153,7 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> { } } -impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> WriteTls for Stream<'a, IO, S> { +impl<'a, IO: AsyncRead + AsyncWrite + Unpin, D: Unpin> WriteTls for Stream<'a, IO, D> { fn write_tls(&mut self, cx: &mut Context) -> io::Result { // TODO writev @@ -179,11 +179,11 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> WriteTls for Str } let mut writer = Writer { io: self.io, cx }; - self.session.write_tls(&mut writer) + self.conn.write_tls(&mut writer) } } -impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> AsyncRead for Stream<'a, IO, S> { +impl<'a, IO: AsyncRead + AsyncWrite + Unpin, D: Unpin> AsyncRead for Stream<'a, IO, D> { fn poll_read( self: Pin<&mut Self>, cx: &mut Context, @@ -191,7 +191,7 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> AsyncRead for Stream<'a ) -> Poll> { let this = self.get_mut(); - while this.session.wants_read() { + while !this.eof && this.conn.wants_read() { match this.complete_inner_io(cx, Focus::Readable) { Poll::Ready(Ok((0, _))) => break, Poll::Ready(Ok(_)) => (), @@ -200,23 +200,28 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> AsyncRead for Stream<'a } } - match this.session.read(buf) { + 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, S: Session> AsyncWrite for Stream<'a, IO, S> { +impl<'a, IO: AsyncRead + AsyncWrite + Unpin, D: Unpin> AsyncWrite for Stream<'a, IO, D> { fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { let this = self.get_mut(); - let len = match this.session.write(buf) { + 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.session.wants_write() { + while this.conn.wants_write() { match this.complete_inner_io(cx, Focus::Writable) { Poll::Ready(Ok(_)) => (), Poll::Pending if len != 0 => break, @@ -229,7 +234,7 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> AsyncWrite for Stream<' Poll::Ready(Ok(len)) } else { // not write zero - match this.session.write(buf) { + 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, @@ -241,8 +246,8 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> AsyncWrite for Stream<' fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let this = self.get_mut(); - this.session.flush()?; - while this.session.wants_write() { + 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) @@ -251,7 +256,7 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> AsyncWrite for Stream<' fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); - while this.session.wants_write() { + while this.conn.wants_write() { ready!(this.complete_inner_io(cx, Focus::Writable))?; } Pin::new(&mut this.io).poll_close(cx) diff --git a/src/rusttls/test_stream.rs b/src/rusttls/test_stream.rs index 8f4d712..c8dc175 100644 --- a/src/rusttls/test_stream.rs +++ b/src/rusttls/test_stream.rs @@ -4,17 +4,20 @@ 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::internal::pemfile::{certs, rsa_private_keys}; -use rustls::{ClientConfig, ClientSession, NoClientAuth, ServerConfig, ServerSession, Session}; +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; -use webpki::DNSNameRef; -struct Good<'a>(&'a mut dyn Session); +struct Good<'a, D>(&'a mut ConnectionCommon); -impl<'a> AsyncRead for Good<'a> { +impl<'a, D> AsyncRead for Good<'a, D> { fn poll_read( mut self: Pin<&mut Self>, _cx: &mut Context<'_>, @@ -24,7 +27,7 @@ impl<'a> AsyncRead for Good<'a> { } } -impl<'a> AsyncWrite for Good<'a> { +impl<'a, D> AsyncWrite for Good<'a, D> { fn poll_write( mut self: Pin<&mut Self>, _cx: &mut Context<'_>, @@ -37,12 +40,16 @@ impl<'a> AsyncWrite for Good<'a> { Poll::Ready(Ok(len)) } - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + 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(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.0.send_close_notify(); + self.poll_flush(cx) } } @@ -82,31 +89,33 @@ impl AsyncWrite for Bad { #[test] fn stream_good() -> io::Result<()> { - const FILE: &'static [u8] = include_bytes!("../../README.md"); - - let fut = async { - 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)?; + block_on(_stream_good()) +} - { - let mut good = Good(&mut server); - let mut stream = Stream::new(&mut good, &mut client); +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 buf = Vec::new(); - stream.read_to_end(&mut buf).await?; - assert_eq!(buf, FILE); - stream.write_all(b"Hello World!").await?; - } + { + let mut good = Good(&mut server); + let mut stream = Stream::new(&mut good, &mut client); - let mut buf = String::new(); - server.read_to_string(&mut buf)?; - assert_eq!(buf, "Hello World!"); + 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?; + } - Ok(()) as io::Result<()> - }; + let mut buf = String::new(); + server.reader().read_to_string(&mut buf)?; + assert_eq!(buf, "Hello World!"); - block_on(fut) + Ok(()) as io::Result<()> } #[test] @@ -114,7 +123,7 @@ 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(1024); + client.set_buffer_limit(Some(1024)); let mut bad = Bad(true); let mut stream = Stream::new(&mut bad, &mut client); @@ -193,12 +202,15 @@ fn stream_eof() -> io::Result<()> { let (mut server, mut client) = make_pair(); future::poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?; - let mut good = Good(&mut server); - let mut stream = Stream::new(&mut good, &mut client).set_eof(true); + let mut eof_stream = Bad(false); + let mut stream = Stream::new(&mut eof_stream, &mut client); let mut buf = Vec::new(); - stream.read_to_end(&mut buf).await?; - assert_eq!(buf.len(), 0); + 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<()> }; @@ -206,39 +218,49 @@ fn stream_eof() -> io::Result<()> { block_on(fut) } -fn make_pair() -> (ServerSession, ClientSession) { +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 mut keys = rsa_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap(); - let mut sconfig = ServerConfig::new(NoClientAuth::new()); - sconfig.set_single_cert(cert, keys.pop().unwrap()).unwrap(); - let server = ServerSession::new(&Arc::new(sconfig)); - - let domain = DNSNameRef::try_from_ascii_str("localhost").unwrap(); - let mut cconfig = ClientConfig::new(); - let mut chain = BufReader::new(Cursor::new(CHAIN)); - cconfig.root_store.add_pem_file(&mut chain).unwrap(); - let client = ClientSession::new(&Arc::new(cconfig), domain); - - (server, client) + 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 ClientSession, - server: &mut ServerSession, + 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.session.is_handshaking() { + if stream.conn.is_handshaking() { ready!(stream.complete_io(cx))?; } - if stream.session.wants_write() { + if stream.conn.wants_write() { ready!(stream.complete_io(cx))?; } diff --git a/src/server.rs b/src/server.rs index 1c1438d..6fb88e4 100644 --- a/src/server.rs +++ b/src/server.rs @@ -5,20 +5,18 @@ use crate::rusttls::stream::Stream; use futures_core::ready; use futures_io::{AsyncRead, AsyncWrite}; -use rustls::ServerSession; +use rustls::ServerConnection; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::{io, mem}; -use rustls::Session; - /// 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) session: ServerSession, + pub(crate) conn: ServerConnection, pub(crate) state: TlsState, } @@ -39,14 +37,14 @@ where if let MidHandshake::Handshaking(stream) = this { let eof = !stream.state.readable(); - let (io, session) = (&mut stream.io, &mut stream.session); + let (io, session) = (&mut stream.io, &mut stream.conn); let mut stream = Stream::new(io, session).set_eof(eof); - if stream.session.is_handshaking() { + if stream.conn.is_handshaking() { ready!(stream.complete_io(cx))?; } - if stream.session.wants_write() { + if stream.conn.wants_write() { ready!(stream.complete_io(cx))?; } } @@ -68,8 +66,7 @@ where buf: &mut [u8], ) -> Poll> { let this = self.get_mut(); - let mut stream = - Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); + let mut stream = Stream::new(&mut this.io, &mut this.conn).set_eof(!this.state.readable()); match this.state { TlsState::Stream | TlsState::WriteShutdown => { @@ -82,7 +79,7 @@ where Poll::Ready(Err(ref err)) if err.kind() == io::ErrorKind::ConnectionAborted => { this.state.shutdown_read(); if this.state.writeable() { - stream.session.send_close_notify(); + stream.conn.send_close_notify(); this.state.shutdown_write(); } Poll::Ready(Ok(0)) @@ -108,27 +105,24 @@ where buf: &[u8], ) -> Poll> { let this = self.get_mut(); - let mut stream = - Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); + 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.session).set_eof(!this.state.readable()); + 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.session.send_close_notify(); + self.conn.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()); + let mut stream = Stream::new(&mut this.io, &mut this.conn).set_eof(!this.state.readable()); stream.as_mut_pin().poll_close(cx) } } diff --git a/src/test_0rtt.rs b/src/test_0rtt.rs index 297e749..1c13396 100644 --- a/src/test_0rtt.rs +++ b/src/test_0rtt.rs @@ -3,7 +3,7 @@ use async_std::net::TcpStream; use async_std::sync::Arc; use futures_executor::block_on; use futures_util::io::{AsyncReadExt, AsyncWriteExt}; -use rustls::ClientConfig; +use rustls::{ClientConfig, OwnedTrustAnchor, RootCertStore}; use std::io; use std::net::ToSocketAddrs; @@ -28,10 +28,19 @@ async fn get( #[test] fn test_0rtt() { - let mut config = ClientConfig::new(); - config - .root_store - .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); + let mut root_certs = RootCertStore::empty(); + root_certs.add_server_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(); + config.enable_early_data = true; let config = Arc::new(config); let domain = "mozilla-modern.badssl.com"; diff --git a/tests/ca.cert b/tests/ca.cert new file mode 100644 index 0000000..04426a9 --- /dev/null +++ b/tests/ca.cert @@ -0,0 +1,19 @@ +-----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----- diff --git a/tests/ca.rsa b/tests/ca.rsa new file mode 100644 index 0000000..8846833 --- /dev/null +++ b/tests/ca.rsa @@ -0,0 +1,28 @@ +-----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----- diff --git a/tests/end.cert b/tests/end.cert index 66f087e..279199f 100644 --- a/tests/end.cert +++ b/tests/end.cert @@ -1,24 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIEADCCAmigAwIBAgICAcgwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhcG9u -eXRvd24gUlNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTE2MTIxMDE3NDIzM1oX -DTIyMDYwMjE3NDIzM1owGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1YDz66+7VD4DL1+/sVHMQ+BbDRgmD -OQlX++mfW8D3QNQm/qDBEbu7T7qqdc9GKDar4WIzBN8SBkzM1EjMGwNnZPV/Tfz0 -qUAR1L/7Zzf1GaFZvWXgksyUpfwvmprH3Iy/dpkETwtPthpTPNlui3hZnm/5kkjR -RWg9HmID4O04Ld6SK313v2ZgrPZbkKvbqlqhUnYWjL3blKVGbpXIsuZzEU9Ph+gH -tPcEhZpFsM6eLe+2TVscIrycMEOTXqAAmO6zZ9sQWtfllu3CElm904H6+jA/9Leg -al72pMmkYr8wWniqDDuijXuCPlVx5EDFFyxBmW18UeDEQaKV3kNfelaTAgMBAAGj -gb4wgbswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwHQYDVR0OBBYEFIYhJkVy -AAKT6cY/ruH1Eu+NNxteMEIGA1UdIwQ7MDmAFNwuPy4Do//Sm5CZDrocHWTrNr96 -oR6kHDAaMRgwFgYDVQQDDA9wb255dG93biBSU0EgQ0GCAXswOwYDVR0RBDQwMoIO -dGVzdHNlcnZlci5jb22CFXNlY29uZC50ZXN0c2VydmVyLmNvbYIJbG9jYWxob3N0 -MA0GCSqGSIb3DQEBCwUAA4IBgQCWV76jfQDZKtfmj45fTwZzoe/PxjWPRbAvSEnt -LRHrPhqQfpMLqpun8uu/w86mHiR/AmiAySMu3zivW6wfGzlRWLi/zCyO6r9LGsgH -bNk5CF642cdZFvn1SiSm1oGXQrolIpcyXu88nUpt74RnY4ETCC1dRQKqxsYufe5T -DOmTm3ChinNW4QRG3yvW6DVuyxVAgZvofyKJOsM3GO6oogIM41aBqZ3UTwmIwp6D -oISdiATslFOzYzjnyXNR8DG8OOkv1ehWuyb8x+hQCZAuogQOWYtCSd6k3kKgd0EM -4CWbt1XDV9ZJwBf2uxZeKuCu/KIy9auNtijAwPsUv9qxuzko018zhl3lWm5p2Sqw -O7fFshU3A6df8hMw7ST6/tgFY7geT88U4iJhfWMwr/CZSRSVMXhTyJgbLIXxKYZj -Ym5v4NAIQP6hI4HixzQaYgrhW6YX6myk+emMjQLRJHT8uHvmT7fuxMJVWWgsCkr1 -C75pRQEagykN/Uzr5e6Tm8sVu88= +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----- diff --git a/tests/end.chain b/tests/end.chain index 7c39013..fbc5e66 100644 --- a/tests/end.chain +++ b/tests/end.chain @@ -1,89 +1,39 @@ -----BEGIN CERTIFICATE----- -MIIGnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255 -dG93biBSU0EgQ0EwHhcNMTYxMjEwMTc0MjMzWhcNMjYxMjA4MTc0MjMzWjAsMSow -KAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G -CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDnfb7vaJbaHEyVTflswWhmHqx5W0NO -KyKbDp2zXEJwDO+NDJq6i1HGnFd/vO4LyjJBU1wUsKtE+m55cfRmUHVuZ2w4n/VF -p7Z7n+SNuvJNcrzDxyKVy4GIZ39zQePnniqtLqXh6eI8Ow6jiMgVxC/wbWcVLKv6 -4RM+2fLjJAC9b27QfjhOlMKVeMOEvPrrpjLSauaHAktQPhuzIAwzxM0+KnvDkWWy -NVqAV/lq6fSO/9vJRhM4E2nxo6yqi7qTdxVxMmKsNn7L6HvjQgx+FXziAUs55Qd9 -cP7etCmPmoefkcgdbxDOIKH8D+DvfacZwngqcnr/q96Ff4uJ13d2OzR1mWVSZ2hE -JQt/BbZBANciqu9OZf3dj6uOOXgFF705ak0GfLtpZpc29M+fVnknXPDSiKFqjzOO -KL+SRGyuNc9ZYjBKkXPJ1OToAs6JSvgDxfOfX0thuo2rslqfpj2qCFugsRIRAqvb -eyFwg+BPM/P/EfauXlAcQtBF04fOi7xN2okCAwEAAaNeMFwwHQYDVR0OBBYEFNwu -Py4Do//Sm5CZDrocHWTrNr96MCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF -BQcDAjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC -BAEAMHZpBqDIUAVFZNw4XbuimXQ4K8q4uePrLGHLb4F/gHbr8kYrU4H+cy4l+xXf -2dlEBdZoqjSF7uXzQg5Fd8Ff3ZgutXd1xeUJnxo0VdpKIhqeaTPqhffC2X6FQQH5 -KrN7NVWQSnUhPNpBFELpmdpY1lHigFW7nytYj0C6VJ4QsbqhfW+n/t+Zgqtfh/Od -ZbclzxFwMM55zRA2HP6IwXS2+d61Jk/RpDHTzhWdjGH4906zGNNMa7slHpCTA9Ju -TrtjEAGt2PBSievBJOHZW80KVAoEX2n9B3ZABaz+uX0VVZG0D2FwhPpUeA57YiXu -qiktZR4Ankph3LabXp4IlAX16qpYsEW8TWE/HLreeqoM0WDoI6rF9qnTpV2KWqBf -ziMYkfSkT7hQ2bWc493lW+QwSxCsuBsDwlrCwAl6jFSf1+jEQx98/8n9rDNyD9dL -PvECmtF30WY98nwZ9/kO2DufQrd0mwSHcIT0pAwl5fimpkwTjj+TTbytO3M4jK5L -tuIzsViQ95BmJQ3XuLdkQ/Ug8rpECYRX5fQX1qXkkvl920ohpKqKyEji1OmfmJ0Z -tZChaEcu3Mp3U+gD4az2ogmle3i/Phz8ZEPFo4/21G5Qd72z0lBgaQIeyyCk5MHt -Yg0vA7X0/w4bz+OJv5tf7zJsPCYSprr+c/7YUJk9Fqu6+g9ZAavI99xFKdGhz4Og -w0trnKNCxYc6+NPopTDbXuY+fo4DK7C0CSae5sKs7013Ne6w4KvgfLKpvlemkGfg -ZA3+1FMXVfFIEH7Cw9cx6F02Sr3k1VrU68oM3wH5nvTUkELOf8nRMlzliQjVCpKB -yFSe9dzRVSFEbMDxChiEulGgNUHj/6wwpg0ZmCwPRHutppT3jkfEqizN5iHb69GH -k6kol6knJofkaL656Q3Oc9o0ZrMlFh1RwmOvAk5fVK0/CV88/phROz2Wdmy5Bz4a -t0vzqFWA54y6+9EEVoOk9SU0CYfpGtpX4URjLK1EUG/l+RR3366Uee6TPrtEZ9cg -56VQMxhSaRNAvJ6DfiSuscSCNJzwuXaMXSZydGYnnP9Tb9p6c1uy1sXdluZkBIcK -CgC+gdDMSNlDn9ghc4xZGkuA8bjzfAYuRuGKmfTt8uuklkjw2b9w3SHjC4/Cmd2W -cFRnzfg2oL6e78hNg2ZGgsLzvb6Lu6/5IhXCO7RitzYf2+HLBbc+YLFsnG3qeGe1 -28yGnXOQd97Cr4+IzFucVy/33gMQkesNUSDFJSq1gE/hGrMgTTMQJ7yC3PRqg0kG -tpqTyKNdM0g1adxlR1qfDPvpUBApkgBbySnMyWEr5+tBuoHUtH2m49oV9YD4odMJ -yJjlGxituO/YNN6O8oANlraG1Q== +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----- -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 -DLxargy39i2aQGg= +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----- diff --git a/tests/end.rsa b/tests/end.rsa index 744bba5..f4d2830 100644 --- a/tests/end.rsa +++ b/tests/end.rsa @@ -1,27 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAtWA8+uvu1Q+Ay9fv7FRzEPgWw0YJgzkJV/vpn1vA90DUJv6g -wRG7u0+6qnXPRig2q+FiMwTfEgZMzNRIzBsDZ2T1f0389KlAEdS/+2c39RmhWb1l -4JLMlKX8L5qax9yMv3aZBE8LT7YaUzzZbot4WZ5v+ZJI0UVoPR5iA+DtOC3ekit9 -d79mYKz2W5Cr26paoVJ2Foy925SlRm6VyLLmcxFPT4foB7T3BIWaRbDOni3vtk1b -HCK8nDBDk16gAJjus2fbEFrX5ZbtwhJZvdOB+vowP/S3oGpe9qTJpGK/MFp4qgw7 -oo17gj5VceRAxRcsQZltfFHgxEGild5DX3pWkwIDAQABAoIBAFDTazlSbGML/pRY -TTWeyIw2UkaA7npIr45C13BJfitw+1nJPK/tDCDDveZ6i3yzLPHZhV5A/HtWzWC1 -9R7nptOrnO83PNN2nPOVQFxzOe+ClXGdQkoagQp5EXHRTspj0WD9I+FUrDDAcOjJ -BAgMJPyi6zlnZAXGDVa3NGyQDoZqwU2k36L4rEsJIkG0NVurZhpiCexNkkf32495 -TOINQ0iKdfJ4iZoEYQ9G+x4NiuAJRCHuIcH76SNfT+Uv3wX0ut5EFPtflnvtdgcp -QVcoKwYdO0+mgO5xqWlBcsujSvgBdiNAGnAxKHWiEaacuIJi4+yYovyEebP6QI2X -Zg/U2wkCgYEA794dE5CPXLOmv6nioVC/ubOESk7vjSlEka/XFbKr4EY794YEqrB1 -8TUqg09Bn3396AS1e6P2shr3bxos5ybhOxDGSLnJ+aC0tRFjd1BPKnA80vZM7ggt -5cjmdD5Zp0tIQTIAAYU5bONQOwj0ej4PE7lny26eLa5vfvCwlrD+rM0CgYEAwZMN -W/5PA2A+EM08IaHic8my0dCunrNLF890ouZnDG99SbgMGvvEsGIcCP1sai702hNh -VgGDxCz6/HUy+4O4YNFVtjY7uGEpfIEcEI7CsLQRP2ggWEFxThZtnEtO8PbM3J/i -qcS6njHdE+0XuCjgZwGgva5xH2pkWFzw/AIpEN8CgYB2HOo2axWc8T2n3TCifI+c -EqCOsqXU3cBM+MgxgASQcCUxMkX0AuZguuxPMmS+85xmdoMi+c8NTqgOhlYcEJIR -sqXgw9OH3zF8g6513w7Md+4Ld4rUHyTypGWOUfF1pmVS7RsBpKdtTdWA7FzuIMbt -0HsiujqbheyTFlPuMAOH9QKBgBWS1gJSrWuq5j/pH7J/4EUXTZ6kq1F0mgHlVRJy -qzlvk38LzA2V0a32wTkfRV3wLcnALzDuqkjK2o4YYb42R+5CZlMQaEd8TKtbmE0g -HAKljuaKLFCpun8BcOXiXsHsP5i3GQPisQnAdOsrmWEk7R2NyORa9LCToutWMGVl -uD3xAoGAA183Vldm+m4KPsKS17t8MbwBryDXvowGzruh/Z+PGA0spr+ke4XxwT1y -kMMP1+5flzmjlAf4+W8LehKuVqvQoMlPn5UVHmSxQ7cGx/O/o6Gbn8Q25/6UT+sM -B1Y0rlLoKG62pnkeXp1O4I57gnClatWRg5qw11a8V8e3jvDKIYM= ------END RSA PRIVATE KEY----- +-----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----- diff --git a/tests/gen_cert_key.bash b/tests/gen_cert_key.bash new file mode 100755 index 0000000..33d8f67 --- /dev/null +++ b/tests/gen_cert_key.bash @@ -0,0 +1,28 @@ +#!/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" \ No newline at end of file diff --git a/tests/google.rs b/tests/google.rs index 5c8a60d..f01418f 100644 --- a/tests/google.rs +++ b/tests/google.rs @@ -13,7 +13,12 @@ fn fetch_google() -> std::io::Result<()> { stream.write_all(b"GET / HTTP/1.0\r\n\r\n").await?; let mut res = vec![]; - stream.read_to_end(&mut res).await?; + // google might answer with a close_notify or not + match stream.read_to_end(&mut res).await { + Ok(_bytes_read) => (), + Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => (), + Err(e) => return Err(e), + } let data = String::from_utf8_lossy(&res); println!("{}", &data); diff --git a/tests/openssl.cfg b/tests/openssl.cfg new file mode 100644 index 0000000..8ed903e --- /dev/null +++ b/tests/openssl.cfg @@ -0,0 +1,13 @@ +[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 = ::1 \ No newline at end of file diff --git a/tests/test.rs b/tests/test.rs index d0f4ffa..c7b5997 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,12 +1,12 @@ +use async_std::channel::bounded; use async_std::io; use async_std::net::{TcpListener, TcpStream}; use async_std::prelude::*; -use async_std::sync::channel; use async_std::task; use async_tls::{TlsAcceptor, TlsConnector}; use lazy_static::lazy_static; -use rustls::internal::pemfile::{certs, rsa_private_keys}; -use rustls::{ClientConfig, ServerConfig}; +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; @@ -16,23 +16,26 @@ const CHAIN: &str = include_str!("end.chain"); const RSA: &str = include_str!("end.rsa"); lazy_static! { - static ref TEST_SERVER: (SocketAddr, &'static str, &'static str) = { + static ref TEST_SERVER: (SocketAddr, &'static str, Vec>) = { let cert = certs(&mut BufReader::new(Cursor::new(CERT))).unwrap(); - let mut keys = rsa_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap(); - - let mut config = ServerConfig::new(rustls::NoClientAuth::new()); - config - .set_single_cert(cert, keys.pop().unwrap()) - .expect("invalid key or certificate"); - let acceptor = TlsAcceptor::from(Arc::new(config)); - - let (send, recv) = channel(1); + 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; + send.send(listener.local_addr()?).await.unwrap(); let mut incoming = listener.incoming(); while let Some(stream) = incoming.next().await { @@ -50,16 +53,16 @@ lazy_static! { }); let addr = task::block_on(async move { recv.recv().await.unwrap() }); - (addr, "localhost", CHAIN) + (addr, "localhost", chain) }; } -fn start_server() -> &'static (SocketAddr, &'static str, &'static str) { +fn start_server() -> &'static (SocketAddr, &'static str, Vec>) { &*TEST_SERVER } async fn start_client(addr: SocketAddr, domain: &str, config: Arc) -> io::Result<()> { - const FILE: &'static [u8] = include_bytes!("../README.md"); + const FILE: &[u8] = include_bytes!("../README.md"); let config = TlsConnector::from(config); let mut buf = vec![0; FILE.len()]; @@ -78,24 +81,28 @@ async fn start_client(addr: SocketAddr, domain: &str, config: Arc) #[test] fn pass() { let (addr, domain, chain) = start_server(); - - let mut config = ClientConfig::new(); - let mut chain = BufReader::new(Cursor::new(chain)); - config.root_store.add_pem_file(&mut chain).unwrap(); - let config = Arc::new(config); - - task::block_on(start_client(addr.clone(), domain, config.clone())).unwrap(); + 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 config = ClientConfig::new(); - let mut chain = BufReader::new(Cursor::new(chain)); - config.root_store.add_pem_file(&mut chain).unwrap(); + 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.clone(), "google.com", config)).is_err()); + assert!(task::block_on(start_client(*addr, "google.com", config)).is_err()); }