diff --git a/.gitignore b/.gitignore index 5ee368e..71e6ab2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,9 @@ Cargo.lock **/*.iml .idea +.code +.vscode +.envrc .*.swp +.direnv/ diff --git a/Cargo.toml b/Cargo.toml index a499c24..137c46c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,12 @@ members = [ "test-cert-gen", ] +[workspace.package] +version = "0.12.0" +authors = ["Stepan Koltsov ", "Aljaž Mur Eržen "] +license = "MIT/Apache-2.0" +repository = "https://github.com/edgedb/rust-tls-api/" +edition = "2018" +keywords = ["tls"] + [patch.crates-io] diff --git a/README.md b/README.md index 68cc0a2..d361e14 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,3 @@ -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/stepancheg/rust-tls-api/CI)](https://github.com/stepancheg/rust-tls-api/actions?query=workflow%3ACI) -[![License](https://img.shields.io/crates/l/tls-api.svg)](https://github.com/stepancheg/rust-tls-api/blob/master/LICENSE) -[![crates.io](https://img.shields.io/crates/v/tls-api.svg)](https://crates.io/crates/tls-api) - # One TLS API to rule them all Supports: @@ -35,7 +31,7 @@ Supports: ## Example -[download-rust-lang-org.rs](https://github.com/stepancheg/rust-tls-api/blob/master/examples/examples/download-rust-lang-org.rs#L66) +[download-rust-lang-org.rs](https://github.com/edgedb/rust-tls-api/blob/master/examples/examples/download-rust-lang-org.rs#L66) contains the implementation of simple TLS client downloading rust-lang.org, which is invoked with four backends. @@ -72,3 +68,10 @@ which is invoked with four backends. * only works on Apple * does not support server side ALPN + + +## Crate maintenance + +This set of crates is currently maintained by the team of EdgeDB. +It has initially been written by + diff --git a/api-test/Cargo.toml b/api-test/Cargo.toml index 9172c5f..07eb52e 100644 --- a/api-test/Cargo.toml +++ b/api-test/Cargo.toml @@ -1,33 +1,30 @@ [package] -name = "tls-api-test" -version = "0.10.0-pre" -authors = ["Stepan Koltsov "] +name = "tls-api-test-2" description = "TLS API without implementation" -license = "MIT/Apache-2.0" -repository = "https://github.com/stepancheg/rust-tls-api/" -keywords = ["tls"] -edition = "2018" + +version = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } +keywords = { workspace = true } [lib] bench = false -[badges] -travis-ci = { repository = "https://github.com/stepancheg/rust-tls-api/", branch = "master" } - [dependencies] -tls-api = { path = "../api", version = "=0.10.0-pre", default-features = false } +tls-api = { path = "../api", version = "0.12.0", default-features = false } log = "0.4" -env_logger = "0.5" +env_logger = "0.11.2" anyhow = "1.0.44" -pem = "0.8.3" +pem = "3.0.4" webpki = "0.22.0" -untrusted = "0.6.*" tokio = { version = "1.2.0", features = ["net", "io-util", "rt", "rt-multi-thread"], optional = true } async-std = { version = "1.9.0", features = ["attributes"], optional = true } -test-cert-gen = { path = "../test-cert-gen", version = "=0.10.0-pre", default-features = false } +test-cert-gen = { path = "../test-cert-gen", package = "test-cert-gen-2", version = "0.12.0", default-features = false } [features] default = ["runtime-tokio"] diff --git a/api-test/README.md b/api-test/README.md index be2f782..308c01d 100644 --- a/api-test/README.md +++ b/api-test/README.md @@ -1,7 +1,3 @@ -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/stepancheg/rust-tls-api/CI)](https://github.com/stepancheg/rust-tls-api/actions?query=workflow%3ACI) -[![License](https://img.shields.io/crates/l/tls-api.svg)](https://github.com/stepancheg/rust-tls-api/blob/master/LICENSE) -[![crates.io](https://img.shields.io/crates/v/tls-api.svg)](https://crates.io/crates/tls-api) - # tls-api-test Test implementation the all tls-api implementations. diff --git a/api-test/build.rs b/api-test/build.rs index 61f68c7..bf9c463 100644 --- a/api-test/build.rs +++ b/api-test/build.rs @@ -2,21 +2,11 @@ use std::env; use std::io::Read; use std::process; -// % rustc +stable --version -// rustc 1.26.0 (a77568041 2018-05-07) -// % rustc +beta --version -// rustc 1.27.0-beta.1 (03fb2f447 2018-05-09) -// % rustc +nightly --version -// rustc 1.27.0-nightly (acd3871ba 2018-05-10) -fn version_is_nightly(version: &str) -> bool { - version.contains("nightly") -} - fn export_rustc_cfg() { let rustc = env::var("RUSTC").expect("RUSTC unset"); let mut child = process::Command::new(rustc) - .args(&["--version"]) + .args(["--version"]) .stdin(process::Stdio::null()) .stdout(process::Stdio::piped()) .spawn() @@ -31,10 +21,6 @@ fn export_rustc_cfg() { .read_to_string(&mut rustc_version) .expect("read_to_string"); assert!(child.wait().expect("wait").success()); - - if version_is_nightly(&rustc_version) { - println!("cargo:rustc-cfg=rustc_nightly"); - } } fn main() { diff --git a/api-test/src/alpn.rs b/api-test/src/alpn.rs index 286bad4..7cf144a 100644 --- a/api-test/src/alpn.rs +++ b/api-test/src/alpn.rs @@ -73,6 +73,12 @@ where assert_eq!(&buf, b"hello"); t!(socket.write_all(b"world").await); + + #[cfg(feature = "runtime-tokio")] + t!(socket.shutdown().await); + + #[cfg(feature = "runtime-async-std")] + t!(socket.close().await); }; block_on(f); }); diff --git a/api-test/src/benches.rs b/api-test/src/benches.rs index 5403a2b..e91c96a 100644 --- a/api-test/src/benches.rs +++ b/api-test/src/benches.rs @@ -1,4 +1,4 @@ -#![cfg(all(rustc_nightly, feature = "runtime-tokio"))] +#![cfg(feature = "runtime-tokio")] use std::thread; diff --git a/api-test/src/client_server.rs b/api-test/src/client_server.rs index 6f7fa4e..7d63ef4 100644 --- a/api-test/src/client_server.rs +++ b/api-test/src/client_server.rs @@ -58,6 +58,12 @@ where assert_eq!(&buf, b"hello"); t!(socket.write_all(b"world").await); + + #[cfg(feature = "runtime-tokio")] + t!(socket.shutdown().await); + + #[cfg(feature = "runtime-async-std")] + t!(socket.close().await); }; block_on(future); }) diff --git a/api-test/src/client_server_dyn.rs b/api-test/src/client_server_dyn.rs index 3f9c961..42fc8bc 100644 --- a/api-test/src/client_server_dyn.rs +++ b/api-test/src/client_server_dyn.rs @@ -51,6 +51,12 @@ async fn test_client_server_dyn_impl( assert_eq!(&buf, b"hello"); t!(socket.write_all(b"world").await); + + #[cfg(feature = "runtime-tokio")] + t!(socket.shutdown().await); + + #[cfg(feature = "runtime-async-std")] + t!(socket.close().await); }; block_on(future); }) diff --git a/api-test/src/gen.rs b/api-test/src/gen.rs index 6a26bd2..fc918cb 100644 --- a/api-test/src/gen.rs +++ b/api-test/src/gen.rs @@ -78,7 +78,7 @@ fn bench_1_dyn(b: &mut test::Bencher) { /// Called from impl crates to generate the common set of tests pub fn gen_tests_and_benches() { - let crate_name = env::var("CARGO_PKG_NAME").unwrap().replace("-", "_"); + let crate_name = env::var("CARGO_PKG_NAME").unwrap().replace('-', "_"); let out_dir = env::var("OUT_DIR").unwrap(); @@ -91,6 +91,4 @@ pub fn gen_tests_and_benches() { let g = format!("// {}generated\n\n{}", "@", g); fs::write(format!("{}/benches_generated.rs", out_dir), g).unwrap(); - - crate::gen_rustc_nightly(); } diff --git a/api-test/src/gen_rustc_nightly.rs b/api-test/src/gen_rustc_nightly.rs deleted file mode 100644 index 317c9f3..0000000 --- a/api-test/src/gen_rustc_nightly.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::env; -use std::io::Read; -use std::process; - -// % rustc +stable --version -// rustc 1.26.0 (a77568041 2018-05-07) -// % rustc +beta --version -// rustc 1.27.0-beta.1 (03fb2f447 2018-05-09) -// % rustc +nightly --version -// rustc 1.27.0-nightly (acd3871ba 2018-05-10) -fn version_is_nightly(version: &str) -> bool { - version.contains("nightly") -} - -// TODO: move into a separate crate -pub fn gen_rustc_nightly() { - let rustc = env::var("RUSTC").expect("RUSTC unset"); - - let mut child = process::Command::new(rustc) - .args(&["--version"]) - .stdin(process::Stdio::null()) - .stdout(process::Stdio::piped()) - .spawn() - .expect("spawn rustc"); - - let mut rustc_version = String::new(); - - child - .stdout - .as_mut() - .expect("stdout") - .read_to_string(&mut rustc_version) - .expect("read_to_string"); - assert!(child.wait().expect("wait").success()); - - if version_is_nightly(&rustc_version) { - println!("cargo:rustc-cfg=rustc_nightly"); - } -} diff --git a/api-test/src/google.rs b/api-test/src/google.rs index e3e6201..5f8fed5 100644 --- a/api-test/src/google.rs +++ b/api-test/src/google.rs @@ -31,7 +31,18 @@ async fn test_google_impl() { t!(tls_stream.write_all(b"GET / HTTP/1.0\r\n\r\n").await); let mut result = vec![]; - t!(tls_stream.read_to_end(&mut result).await); + let res = tls_stream.read_to_end(&mut result).await; + + // Google will not send close_notify and just close the connection. + // This means that they are not confirming to TLS exactly, that connections to google.com + // are vulnerable to truncation attacks and that we need to suppress error about this here. + match res { + Ok(_) => {} + Err(e) + if e.to_string() + .contains("peer closed connection without sending TLS close_notify") => {} + Err(e) => panic!("{}", e), + } println!("{}", String::from_utf8_lossy(&result)); assert!( diff --git a/api-test/src/lib.rs b/api-test/src/lib.rs index 9cee46f..50485e4 100644 --- a/api-test/src/lib.rs +++ b/api-test/src/lib.rs @@ -4,12 +4,11 @@ //! //! Probably you don't need this crate outside of `rust-tls-api` repository. -#![cfg_attr(rustc_nightly, feature(test))] +#![feature(test)] #[macro_use] extern crate log; -#[cfg(rustc_nightly)] extern crate test; use std::any; @@ -36,9 +35,6 @@ pub use version::test_version; mod gen; pub use gen::gen_tests_and_benches; -mod gen_rustc_nightly; -pub(crate) use gen_rustc_nightly::gen_rustc_nightly; - use tls_api::TlsAcceptor; use tls_api::TlsAcceptorBox; use tls_api::TlsAcceptorBuilder; @@ -149,6 +145,7 @@ where )) } +#[allow(dead_code)] fn new_acceptor_from_der_keys() -> A { new_acceptor_builder_from_der_keys::().build().unwrap() } @@ -167,6 +164,7 @@ fn new_acceptor_builder_dyn_from_der_keys(acceptor: &dyn TlsAcceptorType) -> Tls t!(acceptor.builder_from_der_key(keys.cert.get_der(), keys.key.get_der())) } +#[allow(dead_code)] fn new_acceptor_dyn_from_der_keys(acceptor: &dyn TlsAcceptorType) -> TlsAcceptorBox { new_acceptor_builder_dyn_from_der_keys(acceptor) .build() @@ -243,6 +241,7 @@ fn new_connector_builder_dyn_with_root_ca( connector } +#[allow(dead_code)] fn new_connector_dyn_with_root_ca(connector: &dyn TlsConnectorType) -> TlsConnectorBox { new_connector_builder_dyn_with_root_ca(connector) .build() diff --git a/api/Cargo.toml b/api/Cargo.toml index f769453..3da6db0 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -1,25 +1,22 @@ [package] name = "tls-api" -version = "0.10.0-pre" -authors = ["Stepan Koltsov "] description = "TLS API without implementation" -license = "MIT/Apache-2.0" -repository = "https://github.com/stepancheg/rust-tls-api/" -readme = "../README.md" -keywords = ["tls"] -edition = "2018" +readme = "README.md" +version = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } +keywords = { workspace = true } [lib] bench = false -[badges] -travis-ci = { repository = "https://github.com/stepancheg/rust-tls-api/", branch = "master" } - [dependencies] -pem = "0.8.3" +pem = "3.0.4" tempfile = "3.3.0" anyhow = "1.0.44" -thiserror = "1.0.30" +thiserror = "2" # Note technically there's no dependency on async-std futures-util = { version = "0.3.1", features = ["io"], optional = true } diff --git a/api/README.md b/api/README.md index 1e10b0f..200026a 100644 --- a/api/README.md +++ b/api/README.md @@ -1,7 +1,3 @@ -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/stepancheg/rust-tls-api/CI)](https://github.com/stepancheg/rust-tls-api/actions?query=workflow%3ACI) -[![License](https://img.shields.io/crates/l/tls-api.svg)](https://github.com/stepancheg/rust-tls-api/blob/master/LICENSE) -[![crates.io](https://img.shields.io/crates/v/tls-api.svg)](https://crates.io/crates/tls-api) - # tls-api Implementation neutral common denominator TLS API. diff --git a/api/src/acceptor.rs b/api/src/acceptor.rs index 5c863f7..2f7ef47 100644 --- a/api/src/acceptor.rs +++ b/api/src/acceptor.rs @@ -138,10 +138,10 @@ pub trait TlsAcceptor: Sized + Sync + Send + 'static { /// and the stream is ready to send and receive. /// /// This version of `accept` returns a stream parameterized by the underlying socket type. - fn accept_with_socket<'a, S>( - &'a self, + fn accept_with_socket( + &self, stream: S, - ) -> BoxFuture<'a, anyhow::Result>> + ) -> BoxFuture<'_, anyhow::Result>> where S: AsyncSocket + fmt::Debug + Unpin; @@ -153,10 +153,10 @@ pub trait TlsAcceptor: Sized + Sync + Send + 'static { /// This version of `accept` returns a stream parameterized by the underlying socket type. /// /// Practically, [`accept`](Self::accept) is usually enough. - fn accept_impl_tls_stream<'a, S>( - &'a self, + fn accept_impl_tls_stream( + &self, stream: S, - ) -> BoxFuture<'a, anyhow::Result> + ) -> BoxFuture<'_, anyhow::Result> where S: AsyncSocket; @@ -169,7 +169,7 @@ pub trait TlsAcceptor: Sized + Sync + Send + 'static { /// might be useful to obtain some TLS implementation-specific data. /// /// Practically, [`accept`](Self::accept) is usually enough. - fn accept<'a, S>(&'a self, stream: S) -> BoxFuture<'a, anyhow::Result> + fn accept(&self, stream: S) -> BoxFuture<'_, anyhow::Result> where S: AsyncSocket + fmt::Debug + Unpin, { @@ -180,7 +180,7 @@ pub trait TlsAcceptor: Sized + Sync + Send + 'static { /// Common part of all connectors. Poor man replacement for HKT. #[macro_export] macro_rules! spi_acceptor_common { - () => { + ($stream: ty) => { fn accept_with_socket<'a, S>( &'a self, stream: S, @@ -189,7 +189,7 @@ macro_rules! spi_acceptor_common { S: $crate::AsyncSocket, { $crate::BoxFuture::new(async move { - let crate_tls_stream: crate::TlsStream = self.accept_impl(stream).await?; + let crate_tls_stream: $stream = self.accept_impl(stream).await?; Ok($crate::TlsStreamWithSocket::new(crate_tls_stream)) }) } diff --git a/api/src/acceptor_box.rs b/api/src/acceptor_box.rs index fff546b..21cc17e 100644 --- a/api/src/acceptor_box.rs +++ b/api/src/acceptor_box.rs @@ -162,7 +162,7 @@ impl TlsAcceptorBuilderBox { trait TlsAcceptorDyn: Send + Sync + 'static { fn type_dyn(&self) -> &'static dyn TlsAcceptorType; - fn accept<'a>(&'a self, socket: AsyncSocketBox) -> BoxFuture<'a, anyhow::Result>; + fn accept(&self, socket: AsyncSocketBox) -> BoxFuture<'_, anyhow::Result>; } impl TlsAcceptorDyn for A { @@ -170,7 +170,7 @@ impl TlsAcceptorDyn for A { A::TYPE_DYN } - fn accept<'a>(&'a self, socket: AsyncSocketBox) -> BoxFuture<'a, anyhow::Result> { + fn accept(&self, socket: AsyncSocketBox) -> BoxFuture<'_, anyhow::Result> { self.accept(socket) } } @@ -198,10 +198,7 @@ impl TlsAcceptorBox { /// /// This operation returns a future which is resolved when the negotiation is complete, /// and the stream is ready to send and receive. - pub fn accept<'a, S: AsyncSocket>( - &'a self, - socket: S, - ) -> BoxFuture<'a, anyhow::Result> { + pub fn accept(&self, socket: S) -> BoxFuture<'_, anyhow::Result> { self.0.accept(AsyncSocketBox::new(socket)) } } diff --git a/api/src/async_as_sync.rs b/api/src/async_as_sync.rs index 7c8b9fa..ebbd1bd 100644 --- a/api/src/async_as_sync.rs +++ b/api/src/async_as_sync.rs @@ -119,7 +119,7 @@ where /// API-implementation of wrapper stream. /// /// Wrapped object is always [`AsyncIoAsSyncIo`]. - type SyncWrapper: Read + Write + Unpin + Send + 'static; + type SyncWrapper: Read + Write + WriteShutdown + Unpin + Send + 'static; /// Which crates imlpements this? fn impl_info() -> ImplInfo; @@ -137,6 +137,47 @@ where fn get_alpn_protocol(w: &Self::SyncWrapper) -> anyhow::Result>>; } +/// Notify the writer that there will be no more data written. +/// In context of TLS providers, this is great time to send notify_close message. +pub trait WriteShutdown: Write { + /// Initiates or attempts to shut down this writer, returning when + /// the I/O connection has completely shut down. + /// + /// For example this is suitable for implementing shutdown of a + /// TLS connection or calling `TcpStream::shutdown` on a proxied connection. + /// Protocols sometimes need to flush out final pieces of data or otherwise + /// perform a graceful shutdown handshake, reading/writing more data as + /// appropriate. This method is the hook for such protocols to implement the + /// graceful shutdown logic. + /// + /// This `shutdown` method is required by implementers of the + /// `AsyncWrite` trait. Wrappers typically just want to proxy this call + /// through to the wrapped type, and base types will typically implement + /// shutdown logic here or just return `Ok(().into())`. Note that if you're + /// wrapping an underlying `AsyncWrite` a call to `shutdown` implies that + /// transitively the entire stream has been shut down. After your wrapper's + /// shutdown logic has been executed you should shut down the underlying + /// stream. + /// + /// Invocation of a `shutdown` implies an invocation of `flush`. Once this + /// method returns it implies that a flush successfully happened + /// before the shutdown happened. That is, callers don't need to call + /// `flush` before calling `shutdown`. They can rely that by calling + /// `shutdown` any pending buffered data will be written out. + /// + /// # Errors + /// + /// This function can return normal I/O errors through `Err`, described + /// above. Additionally this method may also render the underlying + /// `Write::write` method no longer usable (e.g. will return errors in the + /// future). It's recommended that once `shutdown` is called the + /// `write` method is no longer called. + fn shutdown(&mut self) -> Result<(), io::Error> { + self.flush()?; + Ok(()) + } +} + /// Implementation of `TlsStreamImpl` for APIs using synchronous I/O. pub struct TlsStreamOverSyncIo where @@ -270,13 +311,13 @@ where #[cfg(feature = "runtime-tokio")] fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.get_mut() - .with_context_sync_to_async(cx, |stream| stream.stream.flush()) + .with_context_sync_to_async(cx, |stream| stream.stream.shutdown()) } #[cfg(feature = "runtime-async-std")] fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.get_mut() - .with_context_sync_to_async(cx, |stream| stream.stream.flush()) + .with_context_sync_to_async(cx, |stream| stream.stream.shutdown()) } } diff --git a/api/src/connector.rs b/api/src/connector.rs index afdc0f9..11360eb 100644 --- a/api/src/connector.rs +++ b/api/src/connector.rs @@ -106,10 +106,7 @@ pub trait TlsConnector: Sized + Sync + Send + 'static { /// Connect using default settings. /// /// Shortcut. - fn connect_default<'a, S>( - domain: &'a str, - stream: S, - ) -> BoxFuture<'a, anyhow::Result> + fn connect_default(domain: &str, stream: S) -> BoxFuture<'_, anyhow::Result> where S: AsyncSocket, { @@ -177,7 +174,7 @@ pub trait TlsConnector: Sized + Sync + Send + 'static { /// Common part of all connectors. Poor man replacement for HKT. #[macro_export] macro_rules! spi_connector_common { - () => { + ($stream: ty) => { fn connect_with_socket<'a, S>( &'a self, domain: &'a str, @@ -187,8 +184,7 @@ macro_rules! spi_connector_common { S: $crate::AsyncSocket, { $crate::BoxFuture::new(async move { - let crate_tls_stream: crate::TlsStream = - self.connect_impl(domain, stream).await?; + let crate_tls_stream: $stream = self.connect_impl(domain, stream).await?; Ok($crate::TlsStreamWithSocket::new(crate_tls_stream)) }) } diff --git a/api/src/connector_box.rs b/api/src/connector_box.rs index f489546..4f03b1a 100644 --- a/api/src/connector_box.rs +++ b/api/src/connector_box.rs @@ -71,8 +71,6 @@ impl TlsConnectorType for TlsConnectorTypeImpl { // Connector builder. trait TlsConnectorBuilderDyn: Send + 'static { - fn type_dyn(&self) -> &'static dyn TlsConnectorType; - fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> anyhow::Result<()>; fn set_verify_hostname(&mut self, verify: bool) -> anyhow::Result<()>; @@ -83,10 +81,6 @@ trait TlsConnectorBuilderDyn: Send + 'static { } impl TlsConnectorBuilderDyn for C { - fn type_dyn(&self) -> &'static dyn TlsConnectorType { - ::TYPE_DYN - } - fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> anyhow::Result<()> { self.set_alpn_protocols(protocols) } @@ -138,8 +132,6 @@ impl TlsConnectorBuilderBox { // Connector. trait TlsConnectorDyn: Send + Sync + 'static { - fn type_dyn(&self) -> &'static dyn TlsConnectorType; - fn connect<'a>( &'a self, domain: &'a str, @@ -148,10 +140,6 @@ trait TlsConnectorDyn: Send + Sync + 'static { } impl TlsConnectorDyn for C { - fn type_dyn(&self) -> &'static dyn TlsConnectorType { - C::TYPE_DYN - } - fn connect<'a>( &'a self, domain: &'a str, diff --git a/api/src/lib.rs b/api/src/lib.rs index aa4a8f0..4f4db57 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -15,11 +15,11 @@ //! //! ``` //! # { #![cfg(feature = "runtime-tokio")] -//! use tls_api::{TlsConnector, TlsConnectorBuilder}; +//! use tls_api_2::{TlsConnector, TlsConnectorBuilder}; //! // or async_std::net::TcpStream; //! use tokio::net::TcpStream; -//! # use tls_api::runtime::AsyncWriteExt; -//! # use tls_api::runtime::AsyncReadExt; +//! # use tls_api_2::runtime::AsyncWriteExt; +//! # use tls_api_2::runtime::AsyncReadExt; //! //! async fn download_rust_lang_org() -> anyhow::Result> { //! let stream = TcpStream::connect(("rust-lang.org", 443)).await?; @@ -36,11 +36,11 @@ //! //! ``` //! # { #![cfg(feature = "runtime-tokio")] -//! use tls_api::TlsConnectorType; +//! use tls_api_2::TlsConnectorType; //! // or async_std::net::TcpStream; //! use tokio::net::TcpStream; -//! # use tls_api::runtime::AsyncWriteExt; -//! # use tls_api::runtime::AsyncReadExt; +//! # use tls_api_2::runtime::AsyncWriteExt; +//! # use tls_api_2::runtime::AsyncReadExt; //! //! async fn download_rust_lang_org(connector_type: &dyn TlsConnectorType) -> anyhow::Result> { //! let stream = TcpStream::connect(("rust-lang.org", 443)).await?; @@ -54,7 +54,7 @@ //! ``` //! //! Have a look at working example invoking all implementation -//! [on GitHub](https://github.com/stepancheg/rust-tls-api/blob/master/examples/examples/download-rust-lang-org.rs#L66). +//! [on GitHub](https://github.com/edgedb/rust-tls-api/blob/master/examples/examples/download-rust-lang-org.rs#L66). //! //! There are also two fake implementations: //! * `tls-api-stub` crate which returns an error on any operations, useful to check code compiles diff --git a/api/src/openssl.rs b/api/src/openssl.rs index 794bed4..0a3160d 100644 --- a/api/src/openssl.rs +++ b/api/src/openssl.rs @@ -17,15 +17,9 @@ pub(crate) fn der_to_pkcs12(cert: &[u8], key: &[u8]) -> anyhow::Result<(Vec, let passphrase = "tls-api-123"; let pem_data = pem::encode_many(&[ - pem::Pem { - tag: "CERTIFICATE".to_owned(), - contents: cert.to_owned(), - }, - pem::Pem { - // Technically it can be non-RSA PRIVATE KEY - tag: "RSA PRIVATE KEY".to_owned(), - contents: key.to_owned(), - }, + pem::Pem::new("CERTIFICATE", cert.to_vec()), + // Technically it can be non-RSA PRIVATE KEY + pem::Pem::new("RSA PRIVATE KEY", key.to_vec()), ]); fs::write(&cert_file, pem_data)?; @@ -84,25 +78,25 @@ pub(crate) fn pkcs12_to_der(pkcs12: &[u8], passphrase: &str) -> anyhow::Result<( } let cert_pem = fs::read_to_string(cert_pem_file)?; - let pems = pem::parse_many(cert_pem); + let pems = pem::parse_many(cert_pem)?; let mut certificates: Vec> = pems .iter() - .flat_map(|p| match p.tag.as_str() { - "CERTIFICATE" => Some(p.contents.clone()), + .flat_map(|p| match p.tag() { + "CERTIFICATE" => Some(p.contents().to_vec()), _ => None, }) .collect(); let mut keys: Vec> = pems .iter() - .flat_map(|p| match p.tag.as_str() { - "PRIVATE KEY" | "RSA PRIVATE KEY" => Some(p.contents.clone()), + .flat_map(|p| match p.tag() { + "PRIVATE KEY" | "RSA PRIVATE KEY" => Some(p.contents().to_vec()), _ => None, }) .collect(); if keys.len() != 1 || certificates.len() != 1 { return Err( crate::CommonError::PemFromPkcs12ContainsNotSingleCertKeyPair( - pems.iter().map(|p| p.tag.clone()).collect(), + pems.iter().map(|p| p.tag().to_string()).collect(), ) .into(), ); diff --git a/api/src/socket_box.rs b/api/src/socket_box.rs index 926ea5f..ba33610 100644 --- a/api/src/socket_box.rs +++ b/api/src/socket_box.rs @@ -41,6 +41,7 @@ impl AsyncSocketBox { Pin::new(&mut self.get_mut().0) } + #[allow(dead_code)] fn deref_for_impl_socket(&self) -> &dyn AsyncSocket { &self.0 } diff --git a/api/src/stream.rs b/api/src/stream.rs index 1a726cd..61ce1e9 100644 --- a/api/src/stream.rs +++ b/api/src/stream.rs @@ -26,6 +26,7 @@ impl TlsStream { Pin::new(&mut self.get_mut().0) } + #[allow(dead_code)] fn deref_for_impl_socket(&self) -> &dyn AsyncSocket { &self.0 } diff --git a/api/src/stream_with_socket.rs b/api/src/stream_with_socket.rs index a769897..95eeaa2 100644 --- a/api/src/stream_with_socket.rs +++ b/api/src/stream_with_socket.rs @@ -92,6 +92,7 @@ impl TlsStreamWithSocket { Pin::new(&mut *self.get_mut().0) } + #[allow(dead_code)] fn deref_for_impl_socket(&self) -> &dyn TlsStreamWithUpcastDyn { &*self.0 } diff --git a/api/src/thread_local_context.rs b/api/src/thread_local_context.rs index 43f4878..0a75d7e 100644 --- a/api/src/thread_local_context.rs +++ b/api/src/thread_local_context.rs @@ -3,7 +3,7 @@ use std::ptr; use std::task::Context; thread_local! { - pub static CONTEXT: Cell<*mut ()> = Cell::new(ptr::null_mut()); + pub static CONTEXT: Cell<*mut ()> = const { Cell::new(ptr::null_mut()) }; } struct RestoreOnDrop(*mut ()); diff --git a/ci-gen/Cargo.toml b/ci-gen/Cargo.toml index bb9a932..97272e4 100644 --- a/ci-gen/Cargo.toml +++ b/ci-gen/Cargo.toml @@ -1,10 +1,14 @@ [package] name = "ci-gen" -version = "0.0.0" -authors = ["Stiopa Koltsov "] -edition = "2018" publish = false +version = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } +keywords = { workspace = true } + [dependencies] gh-actions-gen = { git = "https://github.com/stepancheg/gh-actions-gen", rev = "d5fb8ab2a784391794afff6f213a8a4eaf522838" } #gh-actions-gen = { path = "../../gh-actions-gen/gh-actions-gen" } diff --git a/ci-gen/src/main.rs b/ci-gen/src/main.rs index b03d83e..fed1165 100644 --- a/ci-gen/src/main.rs +++ b/ci-gen/src/main.rs @@ -80,11 +80,12 @@ const WINDOWS: Os = Os { fn cargo_doc_job() -> Job { let os = LINUX; - let mut steps = Vec::new(); - steps.push(cargo_cache()); - steps.push(checkout_sources()); - steps.push(rust_install_toolchain(RustToolchain::Stable)); - steps.push(cargo_doc("cargo doc", "")); + let steps = vec![ + cargo_cache(), + checkout_sources(), + rust_install_toolchain(RustToolchain::Stable), + cargo_doc("cargo doc", ""), + ]; Job { id: "cargo-doc".to_owned(), name: "cargo doc".to_owned(), @@ -113,7 +114,6 @@ fn jobs() -> Vec { runs_on: os.ghwf, env: vec![("RUST_BACKTRACE".to_owned(), "1".to_owned())], steps: steps(rt, os, channel), - ..Default::default() }); } } diff --git a/examples/Cargo.toml b/examples/Cargo.toml index d0e7854..e93a01a 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -3,8 +3,8 @@ name = "tls-api-examples" version = "0.0.0" authors = ["Carl Lerche ", "Alex Crichton "] -license = "MIT/Apache-2.0" -repository = "https://github.com/stepancheg/rust-tls-api" +license = { workspace = true } +repository = "https://github.com/edgedb/rust-tls-api" description = """ Example of tls-api """ @@ -13,14 +13,14 @@ publish = false edition = "2018" [dependencies] -tls-api = { path = "../api", default-features = false } +tls-api = { path = "../api", package = "tls-api", default-features = false } tokio = { version = "1.2.0", features = ["full"], optional = true } async-std = { version = "1.9.0", features = ["attributes"], optional = true } -tls-api-native-tls = { path = "../impl-native-tls", default-features = false } -tls-api-rustls = { path = "../impl-rustls", default-features = false } -tls-api-security-framework = { path = "../impl-security-framework", default-features = false } -tls-api-openssl = { path = "../impl-openssl", default-features = false } +tls-api-native-tls = { path = "../impl-native-tls", package = "tls-api-native-tls", default-features = false } +tls-api-rustls = { path = "../impl-rustls", package = "tls-api-rustls", default-features = false } +tls-api-security-framework = { path = "../impl-security-framework", package = "tls-api-security-framework", default-features = false } +tls-api-openssl = { path = "../impl-openssl", package = "tls-api-openssl", default-features = false } [features] default = ["runtime-tokio"] @@ -42,8 +42,8 @@ runtime-tokio = [ ] [dev-dependencies] -env_logger = { version = "0.5", default-features = false } -cfg-if = "0.1" +env_logger = { version = "0.11.2", default-features = false } +cfg-if = "1.0.0" tokio = { version = "1.2.0", features = ["net"] } [target.'cfg(all(not(target_os = "macos"), not(windows), not(target_os = "ios")))'.dev-dependencies] @@ -51,11 +51,11 @@ openssl = "0.10.20" [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dev-dependencies] -security-framework = { version = "2.9.2", features = ["alpn"] } +security-framework = { version = "3.0.1", features = ["alpn"] } [target.'cfg(windows)'.dev-dependencies] advapi32-sys = "0.2" crypt32-sys = "0.2" kernel32-sys = "0.2" schannel = "0.1" -winapi = "0.2" +winapi = "0.3.9" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..8738776 --- /dev/null +++ b/flake.lock @@ -0,0 +1,85 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": [] + }, + "locked": { + "lastModified": 1708928609, + "narHash": "sha256-LcXC2NP/TzHMmJThZGG1e+7rht5HeuZK5WOirIDg+lU=", + "owner": "nix-community", + "repo": "fenix", + "rev": "e928fb6b5179ebd032c19afac5c461ccc0b6de55", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1706830856, + "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1709816690, + "narHash": "sha256-ugzGLZd+LPVigd8psiEpHokxsA6a3kXkVvkpH125FmM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "00f00e0663ee05d58b8f9bdc937018409d49d48a", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1706550542, + "narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "97b17f32362e475016f942bbdfda4a4a72a8a652", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..135e932 --- /dev/null +++ b/flake.nix @@ -0,0 +1,47 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs"; + flake-parts.url = "github:hercules-ci/flake-parts"; + + # provides rust toolchain + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.rust-analyzer-src.follows = ""; + }; + + }; + + outputs = inputs@{ flake-parts, fenix, ... }: + flake-parts.lib.mkFlake {inherit inputs;} { + systems = ["x86_64-linux" "x86_64-darwin" "aarch64-darwin"]; + perSystem = { config, system, pkgs, ... }: + let + fenix_pkgs = fenix.packages.${system}; + + rustToolchain = (fenix_pkgs.complete.withComponents [ + "cargo" + "clippy" + "rust-src" + "rustc" + "rustfmt" + "rust-analyzer" + ]); + + in { + devShells.default = pkgs.mkShell { + buildInputs = [ + pkgs.openssl + pkgs.pkg-config + pkgs.cargo-nextest + rustToolchain + ] + ++ pkgs.lib.optional pkgs.stdenv.isDarwin [ + pkgs.libiconv + pkgs.darwin.apple_sdk.frameworks.CoreServices + pkgs.darwin.apple_sdk.frameworks.SystemConfiguration + ]; + }; + }; + }; +} diff --git a/impl-native-tls/Cargo.toml b/impl-native-tls/Cargo.toml index 55d12be..0b0ef1b 100644 --- a/impl-native-tls/Cargo.toml +++ b/impl-native-tls/Cargo.toml @@ -1,39 +1,38 @@ [package] name = "tls-api-native-tls" -version = "0.10.0-pre" -authors = ["Stepan Koltsov "] description = "TLS API implementation over native-tls crate" -license = "MIT/Apache-2.0" -repository = "https://github.com/stepancheg/rust-tls-api/" -keywords = ["tls"] -edition = "2018" + +version = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } +keywords = { workspace = true } [lib] bench = false -[badges] -travis-ci = { repository = "https://github.com/stepancheg/rust-tls-api/", branch = "master" } - [dependencies] -native-tls = { version ="0.2", features = ["alpn"] } -tokio = { version = "1.2.0", features = [], optional = true } -async-std = { version = "1.9.0", features = ["attributes"], optional = true } -anyhow = "1.0.44" -thiserror = "1.0.30" - -tls-api = { path = "../api", version = "=0.10.0-pre", default-features = false } +native-tls = { version = "0.2", features = ["alpn"] } +tokio = { version = "1.2.0", features = [], optional = true } +async-std = { version = "1.9.0", features = ["attributes"], optional = true } +anyhow = "1.0.44" +thiserror = "2" -# this is needed until package-features is stabelized (issue #5364) -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } +tls-api = { path = "../api", version = "0.12.0", default-features = false } [features] default = ["runtime-tokio"] -runtime-async-std = ["async-std", "tls-api/runtime-async-std", "tls-api-test/runtime-async-std"] +runtime-async-std = [ + "async-std", + "tls-api/runtime-async-std", + "tls-api-test/runtime-async-std", +] runtime-tokio = ["tokio", "tls-api/runtime-tokio", "tls-api-test/runtime-tokio"] [dev-dependencies] -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } -test-cert-gen = { path = "../test-cert-gen", version = "=0.10.0-pre", default-features = false } +tls-api-test = { path = "../api-test", package = "tls-api-test-2", version = "0.12.0", default-features = false } +test-cert-gen = { path = "../test-cert-gen", package = "test-cert-gen-2", version = "0.12.0", default-features = false } [build-dependencies] -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } +tls-api-test = { path = "../api-test", package = "tls-api-test-2", version = "0.12.0", default-features = false } diff --git a/impl-native-tls/README.md b/impl-native-tls/README.md index db2b651..957a318 100644 --- a/impl-native-tls/README.md +++ b/impl-native-tls/README.md @@ -1,5 +1,3 @@ -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/stepancheg/rust-tls-api/CI)](https://github.com/stepancheg/rust-tls-api/actions?query=workflow%3ACI) -[![License](https://img.shields.io/crates/l/tls-api.svg)](https://github.com/stepancheg/rust-tls-api/blob/master/LICENSE) -[![crates.io](https://img.shields.io/crates/v/tls-api.svg)](https://crates.io/crates/tls-api) - # tls-api-native-tls + +Implementation of [`tls-api`](https://crates.io/crates/tls-api) over [native-tls](https://crates.io/crates/native-tls) crate. diff --git a/impl-native-tls/benches/generated.rs b/impl-native-tls/benches/generated.rs index 6dd2433..0bef365 100644 --- a/impl-native-tls/benches/generated.rs +++ b/impl-native-tls/benches/generated.rs @@ -1,7 +1,4 @@ -#![cfg(all(rustc_nightly, feature = "runtime-tokio"))] +#![cfg(feature = "runtime-tokio")] #![feature(test)] include!(concat!(env!("OUT_DIR"), "/benches_generated.rs")); - -#[bench] // Tell Idea this file is a bench -fn dummy(_b: &mut test::Bencher) {} diff --git a/impl-native-tls/examples/client.rs b/impl-native-tls/examples/client.rs index de9b3d5..ebd21a3 100644 --- a/impl-native-tls/examples/client.rs +++ b/impl-native-tls/examples/client.rs @@ -17,7 +17,7 @@ async fn run() { let mut builder = tls_api_native_tls::TlsConnector::builder().unwrap(); builder - .add_root_certificate(&Cert::from_der(fs::read("ca.der").unwrap()).get_der()) + .add_root_certificate(Cert::from_der(fs::read("ca.der").unwrap()).get_der()) .unwrap(); // builder.add_root_certificate(Cert::Der(X509Cert::new(fs::read("/Users/nga/devel/left/rust-security-framework/security-framework/test/server.der").unwrap()))).unwrap(); // builder.builder.danger_accept_invalid_certs(true); diff --git a/impl-native-tls/src/acceptor.rs b/impl-native-tls/src/acceptor.rs index bd27c18..47502d0 100644 --- a/impl-native-tls/src/acceptor.rs +++ b/impl-native-tls/src/acceptor.rs @@ -31,10 +31,10 @@ impl tls_api::TlsAcceptorBuilder for TlsAcceptorBuilder { } impl TlsAcceptor { - fn accept_impl<'a, S>( - &'a self, + fn accept_impl( + &self, stream: S, - ) -> impl Future>> + 'a + ) -> impl Future>> + '_ where S: AsyncSocket, { @@ -69,5 +69,5 @@ impl tls_api::TlsAcceptor for TlsAcceptor { ))) } - spi_acceptor_common!(); + spi_acceptor_common!(crate::TlsStream); } diff --git a/impl-native-tls/src/connector.rs b/impl-native-tls/src/connector.rs index ff98587..0718578 100644 --- a/impl-native-tls/src/connector.rs +++ b/impl-native-tls/src/connector.rs @@ -101,5 +101,5 @@ impl tls_api::TlsConnector for TlsConnector { }) } - spi_connector_common!(); + spi_connector_common!(crate::TlsStream); } diff --git a/impl-native-tls/src/handshake.rs b/impl-native-tls/src/handshake.rs index e585a73..9bd166a 100644 --- a/impl-native-tls/src/handshake.rs +++ b/impl-native-tls/src/handshake.rs @@ -11,6 +11,8 @@ use tls_api::async_as_sync::AsyncIoAsSyncIo; use tls_api::spi::save_context; use tls_api::AsyncSocket; +use crate::stream::NativeTlsStream; + pub(crate) enum HandshakeFuture { Initial(F, AsyncIoAsSyncIo), MidHandshake(native_tls::MidHandshakeTlsStream>), @@ -35,27 +37,23 @@ where let self_mut = self.get_mut(); match mem::replace(self_mut, HandshakeFuture::Done) { HandshakeFuture::Initial(f, stream) => match f(stream) { - Ok(stream) => { - return Poll::Ready(Ok(crate::TlsStream::new(stream))); - } + Ok(stream) => Poll::Ready(Ok(crate::TlsStream::new(NativeTlsStream(stream)))), Err(native_tls::HandshakeError::WouldBlock(mid)) => { *self_mut = HandshakeFuture::MidHandshake(mid); - return Poll::Pending; + Poll::Pending } Err(native_tls::HandshakeError::Failure(e)) => { - return Poll::Ready(Err(anyhow::Error::new(e))) + Poll::Ready(Err(anyhow::Error::new(e))) } }, HandshakeFuture::MidHandshake(stream) => match stream.handshake() { - Ok(stream) => { - return Poll::Ready(Ok(crate::TlsStream::new(stream))); - } + Ok(stream) => Poll::Ready(Ok(crate::TlsStream::new(NativeTlsStream(stream)))), Err(native_tls::HandshakeError::WouldBlock(mid)) => { *self_mut = HandshakeFuture::MidHandshake(mid); - return Poll::Pending; + Poll::Pending } Err(native_tls::HandshakeError::Failure(e)) => { - return Poll::Ready(Err(anyhow::Error::new(e))) + Poll::Ready(Err(anyhow::Error::new(e))) } }, HandshakeFuture::Done => panic!("Future must not be polled after ready"), diff --git a/impl-native-tls/src/stream.rs b/impl-native-tls/src/stream.rs index d89faf0..eb6ad3e 100644 --- a/impl-native-tls/src/stream.rs +++ b/impl-native-tls/src/stream.rs @@ -1,15 +1,18 @@ -use native_tls::TlsStream as native_tls_TlsStream; use std::fmt; +use std::io; +use std::io::Read; +use std::io::Write; use std::marker::PhantomData; use tls_api::async_as_sync::AsyncIoAsSyncIo; use tls_api::async_as_sync::AsyncWrapperOps; use tls_api::async_as_sync::TlsStreamOverSyncIo; +use tls_api::async_as_sync::WriteShutdown; use tls_api::spi_async_socket_impl_delegate; use tls_api::spi_tls_stream_over_sync_io_wrapper; use tls_api::AsyncSocket; use tls_api::ImplInfo; -spi_tls_stream_over_sync_io_wrapper!(TlsStream, native_tls_TlsStream); +spi_tls_stream_over_sync_io_wrapper!(TlsStream, NativeTlsStream); #[derive(Debug)] pub(crate) struct AsyncWrapperOpsImpl(PhantomData<(S, A)>) @@ -22,25 +25,79 @@ where S: fmt::Debug + Unpin + Send + 'static, A: AsyncSocket, { - type SyncWrapper = native_tls::TlsStream>; + type SyncWrapper = NativeTlsStream>; fn impl_info() -> ImplInfo { crate::info() } fn debug(w: &Self::SyncWrapper) -> &dyn fmt::Debug { - w + &w.0 } fn get_mut(w: &mut Self::SyncWrapper) -> &mut AsyncIoAsSyncIo { - w.get_mut() + w.0.get_mut() } fn get_ref(w: &Self::SyncWrapper) -> &AsyncIoAsSyncIo { - w.get_ref() + w.0.get_ref() } fn get_alpn_protocol(w: &Self::SyncWrapper) -> anyhow::Result>> { - w.negotiated_alpn().map_err(anyhow::Error::new) + w.0.negotiated_alpn().map_err(anyhow::Error::new) + } +} + +pub(crate) struct NativeTlsStream(pub(crate) native_tls::TlsStream); + +impl Write for NativeTlsStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.0.write_all(buf) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + self.0.write_fmt(fmt) + } +} + +impl WriteShutdown for NativeTlsStream { + fn shutdown(&mut self) -> Result<(), io::Error> { + self.flush()?; + self.0.shutdown()?; + Ok(()) + } +} + +impl Read for NativeTlsStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.0.read_to_string(buf) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.0.read_exact(buf) } } diff --git a/impl-native-tls/tests/generated.rs b/impl-native-tls/tests/generated.rs index cf4875f..10cd833 100644 --- a/impl-native-tls/tests/generated.rs +++ b/impl-native-tls/tests/generated.rs @@ -1,4 +1 @@ include!(concat!(env!("OUT_DIR"), "/tests_generated.rs")); - -#[test] // Tell Idea this file is a test -fn dummy() {} diff --git a/impl-not-tls/Cargo.toml b/impl-not-tls/Cargo.toml index 88901f2..640b6f6 100644 --- a/impl-not-tls/Cargo.toml +++ b/impl-not-tls/Cargo.toml @@ -1,34 +1,33 @@ [package] name = "tls-api-not-tls" -version = "0.10.0-pre" -authors = ["Stepan Koltsov "] description = "TLS API implementation which returns plain sockets. This is NOT TLS implementation." -license = "MIT/Apache-2.0" -repository = "https://github.com/stepancheg/rust-tls-api/" -keywords = ["tls"] -edition = "2018" + +version = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } +keywords = { workspace = true } [lib] bench = false -[badges] -travis-ci = { repository = "https://github.com/stepancheg/rust-tls-api/", branch = "master" } - [dependencies] -tls-api = { path = "../api", version = "=0.10.0-pre", default-features = false } +tls-api = { path = "../api", version = "0.12.0", default-features = false } -tokio = { version = "1.2.0", features = [], optional = true } +tokio = { version = "1.2.0", features = [], optional = true } async-std = { version = "1.9.0", features = ["attributes"], optional = true } -anyhow = "1.0.44" -thiserror = "1.0.30" - -# this is needed until package-features is stabelized (issue #5364) -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } +anyhow = "1.0.44" +thiserror = "2" [features] default = ["runtime-tokio"] -runtime-async-std = ["async-std", "tls-api/runtime-async-std", "tls-api-test/runtime-async-std"] +runtime-async-std = [ + "async-std", + "tls-api/runtime-async-std", + "tls-api-test/runtime-async-std", +] runtime-tokio = ["tokio", "tls-api/runtime-tokio", "tls-api-test/runtime-tokio"] [dev-dependencies] -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } +tls-api-test = { path = "../api-test", package = "tls-api-test-2", version = "0.12.0", default-features = false } diff --git a/impl-not-tls/README.md b/impl-not-tls/README.md index 292b0f1..21518cf 100644 --- a/impl-not-tls/README.md +++ b/impl-not-tls/README.md @@ -1,7 +1,3 @@ -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/stepancheg/rust-tls-api/CI)](https://github.com/stepancheg/rust-tls-api/actions?query=workflow%3ACI) -[![License](https://img.shields.io/crates/l/tls-api.svg)](https://github.com/stepancheg/rust-tls-api/blob/master/LICENSE) -[![crates.io](https://img.shields.io/crates/v/tls-api.svg)](https://crates.io/crates/tls-api) - # tls-api-not-tls Implementation which returns the (wrapped) socket in `connect` and `accept` operations. No TLS. diff --git a/impl-not-tls/src/acceptor.rs b/impl-not-tls/src/acceptor.rs index df9507b..a53d8d1 100644 --- a/impl-not-tls/src/acceptor.rs +++ b/impl-not-tls/src/acceptor.rs @@ -1,5 +1,3 @@ -use std::future::Future; - use std::fmt; use tls_api::spi_acceptor_common; use tls_api::AsyncSocket; @@ -22,21 +20,18 @@ impl tls_api::TlsAcceptorBuilder for TlsAcceptorBuilder { } fn build(self) -> anyhow::Result { - Ok(TlsAcceptor(self.0)) + Ok(TlsAcceptor(())) } } pub struct TlsAcceptor(pub ()); impl TlsAcceptor { - fn accept_impl<'a, S>( - &'a self, - stream: S, - ) -> impl Future>> + 'a + async fn accept_impl(&self, stream: S) -> anyhow::Result> where S: AsyncSocket + fmt::Debug + Unpin, { - async { Ok(crate::stream::TlsStream(stream)) } + Ok(crate::stream::TlsStream(stream)) } } @@ -59,5 +54,5 @@ impl tls_api::TlsAcceptor for TlsAcceptor { crate::info() } - spi_acceptor_common!(); + spi_acceptor_common!(crate::TlsStream); } diff --git a/impl-not-tls/src/connector.rs b/impl-not-tls/src/connector.rs index a0fe4e0..461fbe8 100644 --- a/impl-not-tls/src/connector.rs +++ b/impl-not-tls/src/connector.rs @@ -31,7 +31,7 @@ impl tls_api::TlsConnectorBuilder for TlsConnectorBuilder { } fn build(self) -> anyhow::Result { - Ok(TlsConnector(self.0)) + Ok(TlsConnector(())) } } @@ -72,5 +72,5 @@ impl tls_api::TlsConnector for TlsConnector { Ok(TlsConnectorBuilder(())) } - spi_connector_common!(); + spi_connector_common!(crate::TlsStream); } diff --git a/impl-openssl/Cargo.toml b/impl-openssl/Cargo.toml index 091b470..ba3a72c 100644 --- a/impl-openssl/Cargo.toml +++ b/impl-openssl/Cargo.toml @@ -1,41 +1,40 @@ [package] name = "tls-api-openssl" -version = "0.10.0-pre" -authors = ["Stepan Koltsov "] description = "TLS API implementation over openssl crate" -license = "MIT/Apache-2.0" -repository = "https://github.com/stepancheg/rust-tls-api/" -keywords = ["tls"] build = "build.rs" -edition = "2018" + +version = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } +keywords = { workspace = true } [lib] bench = false -[badges] -travis-ci = { repository = "https://github.com/stepancheg/rust-tls-api/", branch = "master" } - [dependencies] # To implement OpenSSL version check in build.rs -openssl-sys = { version = "0.9.43" } -openssl = { version = "0.10.20", features = ["v102", "v110"] } -tls-api = { path = "../api", version = "=0.10.0-pre", default-features = false } -tokio = { version = "1.2.0", features = [], optional = true } -async-std = { version = "1.9.0", features = ["attributes"], optional = true } -anyhow = "1.0.44" -thiserror = "1.0.30" - -# this is needed until package-features is stabelized (issue #5364) -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } +openssl-sys = { version = "0.9.43" } +openssl = { version = "0.10.20", features = ["v102", "v110"] } +tls-api = { path = "../api", version = "0.12.0", default-features = false } +tokio = { version = "1.2.0", features = [], optional = true } +async-std = { version = "1.9.0", features = ["attributes"], optional = true } +anyhow = "1.0.44" +thiserror = "2" [features] default = ["runtime-tokio"] -runtime-async-std = ["async-std", "tls-api/runtime-async-std", "tls-api-test/runtime-async-std"] +runtime-async-std = [ + "async-std", + "tls-api/runtime-async-std", + "tls-api-test/runtime-async-std", +] runtime-tokio = ["tokio", "tls-api/runtime-tokio", "tls-api-test/runtime-tokio"] [dev-dependencies] -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } -test-cert-gen = { path = "../test-cert-gen", version = "=0.10.0-pre", default-features = false } +tls-api-test = { path = "../api-test", package = "tls-api-test-2", version = "0.12.0", default-features = false } +test-cert-gen = { path = "../test-cert-gen", package = "test-cert-gen-2", version = "0.12.0", default-features = false } [build-dependencies] -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } +tls-api-test = { path = "../api-test", package = "tls-api-test-2", version = "0.12.0", default-features = false } diff --git a/impl-openssl/README.md b/impl-openssl/README.md index 5f978af..18dc0d8 100644 --- a/impl-openssl/README.md +++ b/impl-openssl/README.md @@ -1,7 +1,3 @@ -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/stepancheg/rust-tls-api/CI)](https://github.com/stepancheg/rust-tls-api/actions?query=workflow%3ACI) -[![License](https://img.shields.io/crates/l/tls-api.svg)](https://github.com/stepancheg/rust-tls-api/blob/master/LICENSE) -[![crates.io](https://img.shields.io/crates/v/tls-api.svg)](https://crates.io/crates/tls-api) - # tls-api-openssl -Implementation of `tls-api` over [openssl](https://crates.io/crates/openssl) crate. +Implementation of [`tls-api`](https://crates.io/crates/tls-api) over [openssl](https://crates.io/crates/openssl) crate. diff --git a/impl-openssl/benches/generated.rs b/impl-openssl/benches/generated.rs index 6dd2433..0bef365 100644 --- a/impl-openssl/benches/generated.rs +++ b/impl-openssl/benches/generated.rs @@ -1,7 +1,4 @@ -#![cfg(all(rustc_nightly, feature = "runtime-tokio"))] +#![cfg(feature = "runtime-tokio")] #![feature(test)] include!(concat!(env!("OUT_DIR"), "/benches_generated.rs")); - -#[bench] // Tell Idea this file is a bench -fn dummy(_b: &mut test::Bencher) {} diff --git a/impl-openssl/examples/client.rs b/impl-openssl/examples/client.rs index bf4c2c2..28d3be3 100644 --- a/impl-openssl/examples/client.rs +++ b/impl-openssl/examples/client.rs @@ -13,7 +13,7 @@ async fn run() { let mut builder = tls_api_openssl::TlsConnector::builder().unwrap(); builder - .add_root_certificate(&Cert::from_der(fs::read("ca.der").unwrap()).get_der()) + .add_root_certificate(Cert::from_der(fs::read("ca.der").unwrap()).get_der()) .unwrap(); let connector = builder.build().unwrap(); connector.connect("localhost", socket).await.unwrap(); diff --git a/impl-openssl/src/acceptor.rs b/impl-openssl/src/acceptor.rs index e011af7..5d558b7 100644 --- a/impl-openssl/src/acceptor.rs +++ b/impl-openssl/src/acceptor.rs @@ -1,4 +1,4 @@ -use openssl::pkcs12::ParsedPkcs12; +use openssl::pkcs12::ParsedPkcs12_2; use tls_api::async_as_sync::AsyncIoAsSyncIo; use tls_api::spi_acceptor_common; @@ -15,9 +15,9 @@ pub struct TlsAcceptorBuilder(pub openssl::ssl::SslAcceptorBuilder); pub struct TlsAcceptor(pub openssl::ssl::SslAcceptor); -fn to_openssl_pkcs12(pkcs12: &[u8], passphrase: &str) -> anyhow::Result { +fn to_openssl_pkcs12(pkcs12: &[u8], passphrase: &str) -> anyhow::Result { let pkcs12 = openssl::pkcs12::Pkcs12::from_der(pkcs12)?; - Ok(pkcs12.parse(passphrase).context("Parse passphrase")?) + pkcs12.parse2(passphrase).context("Parse passphrase") } impl tls_api::TlsAcceptorBuilder for TlsAcceptorBuilder { @@ -53,10 +53,10 @@ impl TlsAcceptorBuilder { } impl TlsAcceptor { - fn accept_impl<'a, S>( - &'a self, + fn accept_impl( + &self, stream: S, - ) -> impl Future>> + 'a + ) -> impl Future>> + '_ where S: AsyncSocket, { @@ -110,7 +110,15 @@ impl tls_api::TlsAcceptor for TlsAcceptor { .map_err(anyhow::Error::new)?; let pkcs12 = to_openssl_pkcs12(pkcs12, passphrase)?; - if let Some(chain) = pkcs12.chain { + + if let Some(cert) = &pkcs12.cert { + builder.set_certificate(cert).map_err(anyhow::Error::new)?; + } + if let Some(pkey) = &pkcs12.pkey { + builder.set_private_key(pkey).map_err(anyhow::Error::new)?; + } + + if let Some(chain) = pkcs12.ca { for x509 in chain { builder .add_extra_chain_cert(x509) @@ -118,15 +126,8 @@ impl tls_api::TlsAcceptor for TlsAcceptor { } } - builder - .set_certificate(&pkcs12.cert) - .map_err(anyhow::Error::new)?; - builder - .set_private_key(&pkcs12.pkey) - .map_err(anyhow::Error::new)?; - Ok(TlsAcceptorBuilder(builder)) } - spi_acceptor_common!(); + spi_acceptor_common!(crate::TlsStream); } diff --git a/impl-openssl/src/connector.rs b/impl-openssl/src/connector.rs index f156d89..63cd3b5 100644 --- a/impl-openssl/src/connector.rs +++ b/impl-openssl/src/connector.rs @@ -111,5 +111,5 @@ impl tls_api::TlsConnector for TlsConnector { }) } - spi_connector_common!(); + spi_connector_common!(crate::TlsStream); } diff --git a/impl-openssl/src/handshake.rs b/impl-openssl/src/handshake.rs index fa70224..be2da66 100644 --- a/impl-openssl/src/handshake.rs +++ b/impl-openssl/src/handshake.rs @@ -11,6 +11,8 @@ use tls_api::async_as_sync::AsyncIoAsSyncIo; use tls_api::spi::save_context; use tls_api::AsyncSocket; +use crate::stream::OpenSSLStream; + pub(crate) enum HandshakeFuture { Initial(F, AsyncIoAsSyncIo), MidHandshake(openssl::ssl::MidHandshakeSslStream>), @@ -35,33 +37,29 @@ where let self_mut = self.get_mut(); match mem::replace(self_mut, HandshakeFuture::Done) { HandshakeFuture::Initial(f, stream) => match f(stream) { - Ok(stream) => { - return Poll::Ready(Ok(crate::TlsStream::new(stream))); - } + Ok(stream) => Poll::Ready(Ok(crate::TlsStream::new(OpenSSLStream(stream)))), Err(openssl::ssl::HandshakeError::WouldBlock(mid)) => { *self_mut = HandshakeFuture::MidHandshake(mid); - return Poll::Pending; + Poll::Pending } Err(openssl::ssl::HandshakeError::Failure(e)) => { - return Poll::Ready(Err(anyhow::Error::new(e.into_error()))) + Poll::Ready(Err(anyhow::Error::new(e.into_error()))) } Err(openssl::ssl::HandshakeError::SetupFailure(e)) => { - return Poll::Ready(Err(anyhow::Error::new(e))) + Poll::Ready(Err(anyhow::Error::new(e))) } }, HandshakeFuture::MidHandshake(stream) => match stream.handshake() { - Ok(stream) => { - return Poll::Ready(Ok(crate::TlsStream::new(stream))); - } + Ok(stream) => Poll::Ready(Ok(crate::TlsStream::new(OpenSSLStream(stream)))), Err(openssl::ssl::HandshakeError::WouldBlock(mid)) => { *self_mut = HandshakeFuture::MidHandshake(mid); - return Poll::Pending; + Poll::Pending } Err(openssl::ssl::HandshakeError::Failure(e)) => { - return Poll::Ready(Err(anyhow::Error::new(e.into_error()))) + Poll::Ready(Err(anyhow::Error::new(e.into_error()))) } Err(openssl::ssl::HandshakeError::SetupFailure(e)) => { - return Poll::Ready(Err(anyhow::Error::new(e))) + Poll::Ready(Err(anyhow::Error::new(e))) } }, HandshakeFuture::Done => panic!("Future must not be polled after ready"), diff --git a/impl-openssl/src/stream.rs b/impl-openssl/src/stream.rs index c22390a..8e4d9fe 100644 --- a/impl-openssl/src/stream.rs +++ b/impl-openssl/src/stream.rs @@ -1,4 +1,7 @@ use std::fmt; +use std::io; +use std::io::Read; +use std::io::Write; use std::marker::PhantomData; use openssl::ssl::SslRef; @@ -6,17 +9,18 @@ use openssl::ssl::SslStream; use tls_api::async_as_sync::AsyncIoAsSyncIo; use tls_api::async_as_sync::AsyncWrapperOps; use tls_api::async_as_sync::TlsStreamOverSyncIo; +use tls_api::async_as_sync::WriteShutdown; use tls_api::spi_async_socket_impl_delegate; use tls_api::spi_tls_stream_over_sync_io_wrapper; use tls_api::AsyncSocket; use tls_api::ImplInfo; -spi_tls_stream_over_sync_io_wrapper!(TlsStream, SslStream); +spi_tls_stream_over_sync_io_wrapper!(TlsStream, OpenSSLStream); impl TlsStream { /// Get the [`SslRef`] object for the stream. pub fn get_ssl_ref(&self) -> &SslRef { - self.0.stream.ssl() + self.0.stream.0.ssl() } } @@ -31,25 +35,82 @@ where S: fmt::Debug + Unpin + Send + 'static, A: AsyncSocket, { - type SyncWrapper = openssl::ssl::SslStream>; + type SyncWrapper = OpenSSLStream>; fn debug(w: &Self::SyncWrapper) -> &dyn fmt::Debug { - w + &w.0 } fn get_mut(w: &mut Self::SyncWrapper) -> &mut AsyncIoAsSyncIo { - w.get_mut() + w.0.get_mut() } fn get_ref(w: &Self::SyncWrapper) -> &AsyncIoAsSyncIo { - w.get_ref() + w.0.get_ref() } fn get_alpn_protocol(w: &Self::SyncWrapper) -> anyhow::Result>> { - Ok(w.ssl().selected_alpn_protocol().map(Vec::from)) + Ok(w.0.ssl().selected_alpn_protocol().map(Vec::from)) } fn impl_info() -> ImplInfo { crate::into() } } + +pub(crate) struct OpenSSLStream(pub(crate) SslStream); + +impl Write for OpenSSLStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.0.write_all(buf) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + self.0.write_fmt(fmt) + } +} + +impl WriteShutdown for OpenSSLStream { + fn shutdown(&mut self) -> Result<(), io::Error> { + self.flush()?; + self.0.shutdown().map_err(|e| { + e.into_io_error() + .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e)) + })?; + Ok(()) + } +} + +impl Read for OpenSSLStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.0.read_to_string(buf) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.0.read_exact(buf) + } +} diff --git a/impl-openssl/tests/generated.rs b/impl-openssl/tests/generated.rs index cf4875f..10cd833 100644 --- a/impl-openssl/tests/generated.rs +++ b/impl-openssl/tests/generated.rs @@ -1,4 +1 @@ include!(concat!(env!("OUT_DIR"), "/tests_generated.rs")); - -#[test] // Tell Idea this file is a test -fn dummy() {} diff --git a/impl-rustls/Cargo.toml b/impl-rustls/Cargo.toml index 979ab07..a44952f 100644 --- a/impl-rustls/Cargo.toml +++ b/impl-rustls/Cargo.toml @@ -1,41 +1,40 @@ [package] name = "tls-api-rustls" -version = "0.10.0-pre" -authors = ["Stepan Koltsov "] description = "TLS API implementation over rustls crate" -license = "MIT/Apache-2.0" -repository = "https://github.com/stepancheg/rust-tls-api/" -keywords = ["tls"] -edition = "2018" + +version = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } +keywords = { workspace = true } [lib] bench = false -[badges] -travis-ci = { repository = "https://github.com/stepancheg/rust-tls-api/", branch = "master" } - [dependencies] -rustls = { version = "0.20.0", features = ["dangerous_configuration"] } -webpki = "0.22.0" -webpki-roots = "0.22.0" -tokio = { version = "1.2.0", features = [], optional = true } -async-std = { version = "1.9.0", features = ["attributes"], optional = true } -anyhow = "1.0.44" -thiserror = "1.0.30" - -tls-api = { path = "../api", version = "=0.10.0-pre", default-features = false } +rustls = { version = "0.23.5", default-features = false, features = ["std"] } +webpki = "0.22.0" +webpki-roots = "0.26.1" +tokio = { version = "1.2.0", features = [], optional = true } +async-std = { version = "1.9.0", features = ["attributes"], optional = true } +anyhow = "1.0.44" +thiserror = "2" -# this is needed until package-features is stabelized (issue #5364) -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } +tls-api = { path = "../api", version = "0.12.0", default-features = false } [features] default = ["runtime-tokio"] -runtime-async-std = ["async-std", "tls-api/runtime-async-std", "tls-api-test/runtime-async-std"] +runtime-async-std = [ + "async-std", + "tls-api/runtime-async-std", + "tls-api-test/runtime-async-std", +] runtime-tokio = ["tokio", "tls-api/runtime-tokio", "tls-api-test/runtime-tokio"] [dev-dependencies] -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } -test-cert-gen = { path = "../test-cert-gen", version = "=0.10.0-pre", default-features = false } +tls-api-test = { path = "../api-test", package = "tls-api-test-2", version = "0.12.0", default-features = false } +test-cert-gen = { path = "../test-cert-gen", package = "test-cert-gen-2", version = "0.12.0", default-features = false } [build-dependencies] -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } +tls-api-test = { path = "../api-test", package = "tls-api-test-2", version = "0.12.0", default-features = false } diff --git a/impl-rustls/README.md b/impl-rustls/README.md index 3de1f44..40dd79c 100644 --- a/impl-rustls/README.md +++ b/impl-rustls/README.md @@ -1,7 +1,3 @@ -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/stepancheg/rust-tls-api/CI)](https://github.com/stepancheg/rust-tls-api/actions?query=workflow%3ACI) -[![License](https://img.shields.io/crates/l/tls-api.svg)](https://github.com/stepancheg/rust-tls-api/blob/master/LICENSE) -[![crates.io](https://img.shields.io/crates/v/tls-api.svg)](https://crates.io/crates/tls-api) - # tls-api-rustls -Implementation of tls-api over [rustls](https://crates.io/crates/rustls). +Implementation of [`tls-api`](https://crates.io/crates/tls-api) over [rustls](https://crates.io/crates/rustls) crate. \ No newline at end of file diff --git a/impl-rustls/benches/generated.rs b/impl-rustls/benches/generated.rs index 6dd2433..0bef365 100644 --- a/impl-rustls/benches/generated.rs +++ b/impl-rustls/benches/generated.rs @@ -1,7 +1,4 @@ -#![cfg(all(rustc_nightly, feature = "runtime-tokio"))] +#![cfg(feature = "runtime-tokio")] #![feature(test)] include!(concat!(env!("OUT_DIR"), "/benches_generated.rs")); - -#[bench] // Tell Idea this file is a bench -fn dummy(_b: &mut test::Bencher) {} diff --git a/impl-rustls/examples/client.rs b/impl-rustls/examples/client.rs index 06f4630..12e1871 100644 --- a/impl-rustls/examples/client.rs +++ b/impl-rustls/examples/client.rs @@ -15,7 +15,7 @@ async fn run() { let mut builder = tls_api_rustls::TlsConnector::builder().unwrap(); builder - .add_root_certificate(&Cert::from_der(fs::read("ca.der").unwrap()).get_der()) + .add_root_certificate(Cert::from_der(fs::read("ca.der").unwrap()).get_der()) .unwrap(); let connector = builder.build().unwrap(); println!("connector ready"); diff --git a/impl-rustls/examples/server.rs b/impl-rustls/examples/server.rs index 759138f..cf28bab 100644 --- a/impl-rustls/examples/server.rs +++ b/impl-rustls/examples/server.rs @@ -12,7 +12,8 @@ use test_cert_gen::pem_to_cert_key_pair; use tls_api::runtime::AsyncWriteExt; async fn run() { - let (cert, key) = pem_to_cert_key_pair(fs::read_to_string("server.pem").unwrap().as_bytes()); + let (cert, key) = + pem_to_cert_key_pair(fs::read_to_string("server.pem").unwrap().as_bytes()).unwrap(); let builder = tls_api_rustls::TlsAcceptor::builder_from_der_key(cert.get_der(), key.get_der()).unwrap(); @@ -24,7 +25,7 @@ async fn run() { let socket = listener.accept().await.unwrap().0; let mut socket = acceptor.accept(socket).await.unwrap(); - socket.write(b"hello\n").await.unwrap(); + socket.write_all(b"hello\n").await.unwrap(); } fn main() { diff --git a/impl-rustls/src/acceptor.rs b/impl-rustls/src/acceptor.rs index c389a83..496a794 100644 --- a/impl-rustls/src/acceptor.rs +++ b/impl-rustls/src/acceptor.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::sync::Arc; use rustls::StreamOwned; @@ -22,7 +23,7 @@ impl tls_api::TlsAcceptorBuilder for TlsAcceptorBuilder { type Underlying = rustls::ServerConfig; fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> anyhow::Result<()> { - self.0.alpn_protocols = protocols.into_iter().map(|p| p.to_vec()).collect(); + self.0.alpn_protocols = protocols.iter().map(|p| p.to_vec()).collect(); Ok(()) } @@ -36,15 +37,15 @@ impl tls_api::TlsAcceptorBuilder for TlsAcceptorBuilder { } impl TlsAcceptor { - pub fn accept_impl<'a, S>( - &'a self, + pub fn accept_impl( + &self, stream: S, - ) -> impl Future>> + 'a + ) -> impl Future>> + '_ where S: AsyncSocket, { let conn = rustls::ServerConnection::new(self.0.clone()); - let conn = match conn.map_err(|e| anyhow::Error::new(e)) { + let conn = match conn.map_err(anyhow::Error::new) { Ok(conn) => conn, Err(e) => return BoxFuture::new(async { Err(e) }), }; @@ -79,14 +80,17 @@ impl tls_api::TlsAcceptor for TlsAcceptor { } fn builder_from_der_key(cert: &[u8], key: &[u8]) -> anyhow::Result { - let cert = rustls::Certificate(cert.to_vec()); + let cert = rustls::pki_types::CertificateDer::from(cert.to_vec()); let config = rustls::ServerConfig::builder() - .with_safe_defaults() .with_no_client_auth() - .with_single_cert(vec![cert], rustls::PrivateKey(key.to_vec())) + .with_single_cert( + vec![cert], + rustls::pki_types::PrivateKeyDer::try_from(key.to_vec()) + .map_err(|x| anyhow::anyhow!(x))?, + ) .map_err(anyhow::Error::new)?; Ok(TlsAcceptorBuilder(config)) } - spi_acceptor_common!(); + spi_acceptor_common!(crate::TlsStream); } diff --git a/impl-rustls/src/connector.rs b/impl-rustls/src/connector.rs index 9c9b24c..2453589 100644 --- a/impl-rustls/src/connector.rs +++ b/impl-rustls/src/connector.rs @@ -1,6 +1,9 @@ use std::convert::TryFrom; use std::sync::Arc; +use rustls::crypto::verify_tls12_signature; +use rustls::crypto::verify_tls13_signature; +use rustls::crypto::WebPkiSupportedAlgorithms; use rustls::StreamOwned; use tls_api::async_as_sync::AsyncIoAsSyncIo; @@ -33,44 +36,75 @@ impl tls_api::TlsConnectorBuilder for TlsConnectorBuilder { } fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> anyhow::Result<()> { - self.config.alpn_protocols = protocols.into_iter().map(|p: &&[u8]| p.to_vec()).collect(); + self.config.alpn_protocols = protocols.iter().map(|p: &&[u8]| p.to_vec()).collect(); Ok(()) } fn set_verify_hostname(&mut self, verify: bool) -> anyhow::Result<()> { if !verify { - struct NoCertificateVerifier; + #[derive(Debug)] + struct NoCertificateServerVerifier { + supported: WebPkiSupportedAlgorithms, + } - impl rustls::client::ServerCertVerifier for NoCertificateVerifier { + impl rustls::client::danger::ServerCertVerifier for NoCertificateServerVerifier { fn verify_server_cert( &self, - _end_entity: &rustls::Certificate, - _intermediates: &[rustls::Certificate], - _server_name: &rustls::ServerName, - _scts: &mut dyn Iterator, + _end_entity: &rustls::pki_types::CertificateDer<'_>, + _intermediates: &[rustls::pki_types::CertificateDer<'_>], + _server_name: &rustls::pki_types::ServerName<'_>, _ocsp_response: &[u8], - _now: std::time::SystemTime, - ) -> Result { - Ok(rustls::client::ServerCertVerified::assertion()) + _now: rustls::pki_types::UnixTime, + ) -> Result + { + Ok(rustls::client::danger::ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &rustls::pki_types::CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result + { + verify_tls12_signature(message, cert, dss, &self.supported) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &rustls::pki_types::CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result + { + verify_tls13_signature(message, cert, dss, &self.supported) + } + + fn supported_verify_schemes(&self) -> Vec { + self.supported.supported_schemes() } } + let no_cert_verifier = NoCertificateServerVerifier { + supported: rustls::crypto::CryptoProvider::get_default() + .unwrap() + .signature_verification_algorithms, + }; + self.config .dangerous() - .set_certificate_verifier(Arc::new(NoCertificateVerifier)); + .set_certificate_verifier(Arc::new(no_cert_verifier)); self.verify_hostname = false; - } else { - if !self.verify_hostname { - return Err(crate::Error::VerifyHostnameTrue.into()); - } + } else if !self.verify_hostname { + return Err(crate::Error::VerifyHostnameTrue.into()); } Ok(()) } fn add_root_certificate(&mut self, cert: &[u8]) -> anyhow::Result<()> { - let cert = rustls::Certificate(cert.to_vec()); - self.root_store.add(&cert).map_err(anyhow::Error::new)?; + let cert = rustls::pki_types::CertificateDer::from(cert); + self.root_store.add(cert).map_err(anyhow::Error::new)?; Ok(()) } @@ -78,14 +112,12 @@ impl tls_api::TlsConnectorBuilder for TlsConnectorBuilder { let mut config = self.config; if !self.root_store.is_empty() { let mut new_config = rustls::ClientConfig::builder() - .with_safe_defaults() .with_root_certificates(self.root_store) .with_no_client_auth(); new_config.alpn_protocols = config.alpn_protocols; - new_config.session_storage = config.session_storage; + new_config.resumption = config.resumption; new_config.max_fragment_size = config.max_fragment_size; new_config.client_auth_cert_resolver = config.client_auth_cert_resolver; - new_config.enable_tickets = config.enable_tickets; new_config.enable_sni = config.enable_sni; new_config.key_log = config.key_log; new_config.enable_early_data = config.enable_early_data; @@ -106,13 +138,13 @@ impl TlsConnector { where S: AsyncSocket, { - let dns_name = rustls::ServerName::try_from(domain); - let dns_name = match dns_name.map_err(|_| anyhow::Error::new(webpki::InvalidDnsNameError)) { - Ok(dns_name) => dns_name, - Err(e) => return BoxFuture::new(async { Err(e) }), + let dns_name = rustls::pki_types::ServerName::try_from(domain); + let dns_name = match dns_name { + Ok(dns_name) => dns_name.to_owned(), + Err(e) => return BoxFuture::new(async { Err(anyhow::anyhow!(e)) }), }; let conn = rustls::ClientConnection::new(self.config.clone(), dns_name); - let conn = match conn.map_err(|e| anyhow::Error::new(e)) { + let conn = match conn.map_err(anyhow::Error::new) { Ok(conn) => conn, Err(e) => return BoxFuture::new(async { Err(e) }), }; @@ -145,15 +177,8 @@ impl tls_api::TlsConnector for TlsConnector { fn builder() -> anyhow::Result { let mut roots = rustls::RootCertStore::empty(); - roots.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { - rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - })); + roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); let config = rustls::ClientConfig::builder() - .with_safe_defaults() .with_root_certificates(roots) .with_no_client_auth(); Ok(TlsConnectorBuilder { @@ -163,5 +188,5 @@ impl tls_api::TlsConnector for TlsConnector { }) } - spi_connector_common!(); + spi_connector_common!(crate::TlsStream); } diff --git a/impl-rustls/src/handshake.rs b/impl-rustls/src/handshake.rs index 0f40138..211a972 100644 --- a/impl-rustls/src/handshake.rs +++ b/impl-rustls/src/handshake.rs @@ -33,14 +33,12 @@ where // sanity check assert!(stream.0.stream.is_handshaking()); match stream.0.stream.complete_io() { - Ok(_) => { - return Poll::Ready(Ok(stream)); - } + Ok(_) => Poll::Ready(Ok(stream)), Err(e) if e.kind() == io::ErrorKind::WouldBlock => { *self_mut = HandshakeFuture::MidHandshake(stream); - return Poll::Pending; + Poll::Pending } - Err(e) => return Poll::Ready(Err(anyhow::Error::new(e))), + Err(e) => Poll::Ready(Err(anyhow::Error::new(e))), } } HandshakeFuture::Done => panic!("Future must not be polled after ready"), diff --git a/impl-rustls/src/rustls_utils.rs b/impl-rustls/src/rustls_utils.rs index 305a0e6..a935a41 100644 --- a/impl-rustls/src/rustls_utils.rs +++ b/impl-rustls/src/rustls_utils.rs @@ -7,6 +7,7 @@ use std::io::IoSlice; use std::io::IoSliceMut; use std::io::Read; use std::io::Write; +use tls_api::async_as_sync::WriteShutdown; pub enum RustlsSessionRef<'a> { Client(&'a ClientConnection), @@ -102,6 +103,17 @@ impl Write for RustlsStream { } } +impl WriteShutdown for RustlsStream { + fn shutdown(&mut self) -> Result<(), io::Error> { + match self { + RustlsStream::Server(s) => s.conn.send_close_notify(), + RustlsStream::Client(s) => s.conn.send_close_notify(), + } + self.flush()?; + Ok(()) + } +} + impl Read for RustlsStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { match self { diff --git a/impl-rustls/tests/generated.rs b/impl-rustls/tests/generated.rs index cf4875f..10cd833 100644 --- a/impl-rustls/tests/generated.rs +++ b/impl-rustls/tests/generated.rs @@ -1,4 +1 @@ include!(concat!(env!("OUT_DIR"), "/tests_generated.rs")); - -#[test] // Tell Idea this file is a test -fn dummy() {} diff --git a/impl-rustls/tests/test.rs b/impl-rustls/tests/test.rs index 1c3fa8b..75b967f 100644 --- a/impl-rustls/tests/test.rs +++ b/impl-rustls/tests/test.rs @@ -10,9 +10,7 @@ fn connect_bad_hostname() { .downcast_ref() .expect("rustls::TLSError"); match err { - rustls::Error::InvalidCertificateData(e) => { - assert_eq!(e, "invalid peer certificate: CertNotValidForName"); - } + rustls::Error::InvalidCertificate(rustls::CertificateError::NotValidForName) => {} err => panic!("wrong error: {:?}", err), } }); diff --git a/impl-security-framework/Cargo.toml b/impl-security-framework/Cargo.toml index 1a0b01a..49b0695 100644 --- a/impl-security-framework/Cargo.toml +++ b/impl-security-framework/Cargo.toml @@ -1,34 +1,29 @@ [package] name = "tls-api-security-framework" -version = "0.10.0-pre" -authors = ["Stepan Koltsov "] description = "TLS API implementation over rustls crate" -license = "MIT/Apache-2.0" -repository = "https://github.com/stepancheg/rust-tls-api/" -keywords = ["tls"] -edition = "2018" + +version = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } +keywords = { workspace = true } [lib] bench = false -[badges] -travis-ci = { repository = "https://github.com/stepancheg/rust-tls-api/", branch = "master" } - [dependencies] -tokio = { version = "1.2.0", features = [], optional = true } +tokio = { version = "1.2.0", features = [], optional = true } async-std = { version = "1.9.0", features = ["attributes"], optional = true } -void = "1.0.2" -anyhow = "1.0.44" -thiserror = "1.0.30" - -tls-api = { path = "../api", version = "=0.10.0-pre", default-features = false } -tls-api-stub = { path = "../impl-stub", version = "=0.10.0-pre", default-features = false } +void = "1.0.2" +anyhow = "1.0.44" +thiserror = "2" -# this is needed until package-features is stabelized (issue #5364) -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } +tls-api = { path = "../api", version = "0.12.0", default-features = false } +tls-api-stub = { path = "../impl-stub", version = "0.12.0", default-features = false } [target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies] -security-framework = { version = "2.9.2", features = ["alpn"] } +security-framework = { version = "3.0.1", features = ["alpn"] } [features] default = ["runtime-tokio"] @@ -46,8 +41,8 @@ runtime-tokio = [ ] [dev-dependencies] -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } -test-cert-gen = { path = "../test-cert-gen", version = "=0.10.0-pre", default-features = false } +tls-api-test = { path = "../api-test", package = "tls-api-test-2", version = "0.12.0", default-features = false } +test-cert-gen = { path = "../test-cert-gen", package = "test-cert-gen-2", version = "0.12.0", default-features = false } [build-dependencies] -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } +tls-api-test = { path = "../api-test", package = "tls-api-test-2", version = "0.12.0", default-features = false } diff --git a/impl-security-framework/README.md b/impl-security-framework/README.md index ed16f36..8cde69c 100644 --- a/impl-security-framework/README.md +++ b/impl-security-framework/README.md @@ -1,7 +1,3 @@ -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/stepancheg/rust-tls-api/CI)](https://github.com/stepancheg/rust-tls-api/actions?query=workflow%3ACI) -[![License](https://img.shields.io/crates/l/tls-api.svg)](https://github.com/stepancheg/rust-tls-api/blob/master/LICENSE) -[![crates.io](https://img.shields.io/crates/v/tls-api.svg)](https://crates.io/crates/tls-api) - # tls-api-security-framework -tls-api implementation over security-framework crate. +Implementation of [`tls-api`](https://crates.io/crates/tls-api) over [security-framework](https://crates.io/crates/security-framework) crate. diff --git a/impl-security-framework/src/acceptor.rs b/impl-security-framework/src/acceptor.rs index dc05553..dd80dd5 100644 --- a/impl-security-framework/src/acceptor.rs +++ b/impl-security-framework/src/acceptor.rs @@ -71,10 +71,10 @@ fn pkcs12_to_sf_objects( } impl TlsAcceptor { - fn accept_impl<'a, S>( - &'a self, + fn accept_impl( + &self, stream: S, - ) -> impl Future>> + 'a + ) -> impl Future>> + '_ where S: AsyncSocket, { @@ -125,5 +125,5 @@ impl tls_api::TlsAcceptor for TlsAcceptor { } } - spi_acceptor_common!(); + spi_acceptor_common!(crate::TlsStream); } diff --git a/impl-security-framework/src/connector.rs b/impl-security-framework/src/connector.rs index 67cca3d..43a06d4 100644 --- a/impl-security-framework/src/connector.rs +++ b/impl-security-framework/src/connector.rs @@ -127,5 +127,5 @@ impl tls_api::TlsConnector for TlsConnector { } } - spi_connector_common!(); + spi_connector_common!(crate::TlsStream); } diff --git a/impl-security-framework/tests/generated.rs b/impl-security-framework/tests/generated.rs index cf4875f..10cd833 100644 --- a/impl-security-framework/tests/generated.rs +++ b/impl-security-framework/tests/generated.rs @@ -1,4 +1 @@ include!(concat!(env!("OUT_DIR"), "/tests_generated.rs")); - -#[test] // Tell Idea this file is a test -fn dummy() {} diff --git a/impl-stub/Cargo.toml b/impl-stub/Cargo.toml index 3e80569..c671b2a 100644 --- a/impl-stub/Cargo.toml +++ b/impl-stub/Cargo.toml @@ -1,30 +1,25 @@ [package] name = "tls-api-stub" -version = "0.10.0-pre" -authors = ["Stepan Koltsov "] description = "TLS API implementation that returns error on any operation" -license = "MIT/Apache-2.0" -repository = "https://github.com/stepancheg/rust-tls-api/" -keywords = ["tls"] -edition = "2018" + +version = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } +keywords = { workspace = true } [lib] bench = false -[badges] -travis-ci = { repository = "https://github.com/stepancheg/rust-tls-api/", branch = "master" } - [dependencies] void = "1.0.2" tokio = { version = "1.2.0", features = [], optional = true } async-std = { version = "1.9.0", features = ["attributes"], optional = true } anyhow = "1.0.44" -thiserror = "1.0.30" - -tls-api = { path = "../api", version = "=0.10.0-pre", default-features = false } +thiserror = "2" -# this is needed until package-features is stabelized (issue #5364) -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } +tls-api = { path = "../api", version = "0.12.0", default-features = false } [features] default = ["runtime-tokio"] @@ -32,4 +27,4 @@ runtime-async-std = ["tokio", "tls-api/runtime-async-std", "tls-api-test/runtime runtime-tokio = ["async-std", "tls-api/runtime-tokio", "tls-api-test/runtime-tokio"] [dev-dependencies] -tls-api-test = { path = "../api-test", version = "=0.10.0-pre", default-features = false } +tls-api-test = { path = "../api-test", package = "tls-api-test-2", version = "0.12.0", default-features = false } diff --git a/impl-stub/README.md b/impl-stub/README.md index ef90abe..56feaf8 100644 --- a/impl-stub/README.md +++ b/impl-stub/README.md @@ -1,7 +1,3 @@ -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/stepancheg/rust-tls-api/CI)](https://github.com/stepancheg/rust-tls-api/actions?query=workflow%3ACI) -[![License](https://img.shields.io/crates/l/tls-api.svg)](https://github.com/stepancheg/rust-tls-api/blob/master/LICENSE) -[![crates.io](https://img.shields.io/crates/v/tls-api.svg)](https://crates.io/crates/tls-api) - ## tls-api-stub Stub implementation of tls-api. All operations return an error. diff --git a/impl-stub/src/acceptor.rs b/impl-stub/src/acceptor.rs index 6628697..afb0b8f 100644 --- a/impl-stub/src/acceptor.rs +++ b/impl-stub/src/acceptor.rs @@ -6,7 +6,6 @@ use tls_api::AsyncSocketBox; use tls_api::ImplInfo; use crate::Error; -use std::future::Future; /// Non-instantiatable. pub struct TlsAcceptorBuilder(Void); @@ -32,14 +31,11 @@ impl tls_api::TlsAcceptorBuilder for TlsAcceptorBuilder { } impl TlsAcceptor { - fn accept_impl<'a, S>( - &'a self, - _stream: S, - ) -> impl Future>> + 'a + async fn accept_impl(&self, _stream: S) -> anyhow::Result> where S: AsyncSocket, { - async { Err(anyhow::Error::new(Error)) } + Err(anyhow::Error::new(Error)) } } @@ -62,5 +58,5 @@ impl tls_api::TlsAcceptor for TlsAcceptor { crate::info() } - spi_acceptor_common!(); + spi_acceptor_common!(crate::TlsStream); } diff --git a/impl-stub/src/connector.rs b/impl-stub/src/connector.rs index 6c16ab4..fc11bcc 100644 --- a/impl-stub/src/connector.rs +++ b/impl-stub/src/connector.rs @@ -6,7 +6,6 @@ use tls_api::ImplInfo; use void::Void; use crate::Error; -use std::future::Future; /// Non-instantiatable. pub struct TlsConnectorBuilder(Void); @@ -40,15 +39,15 @@ impl tls_api::TlsConnectorBuilder for TlsConnectorBuilder { } impl TlsConnector { - fn connect_impl<'a, S>( + async fn connect_impl<'a, S>( &'a self, _domain: &'a str, _stream: S, - ) -> impl Future>> + 'a + ) -> anyhow::Result> where S: AsyncSocket, { - async { Err(anyhow::Error::new(Error)) } + Err(anyhow::Error::new(Error)) } } @@ -73,5 +72,5 @@ impl tls_api::TlsConnector for TlsConnector { Err(anyhow::Error::new(Error)) } - spi_connector_common!(); + spi_connector_common!(crate::TlsStream); } diff --git a/impl-stub/src/lib.rs b/impl-stub/src/lib.rs index e900ee9..4d4e659 100644 --- a/impl-stub/src/lib.rs +++ b/impl-stub/src/lib.rs @@ -5,7 +5,7 @@ //! Can be useful when you need a type parameter of type e. g. `TlsConnector`: //! //! ``` -//! use tls_api_stub::TlsConnector; +//! use tls_api_stub_2::TlsConnector; //! //! let no_connector: Option = None; //! ``` diff --git a/interop/Cargo.toml b/interop/Cargo.toml index a63a630..fb905df 100644 --- a/interop/Cargo.toml +++ b/interop/Cargo.toml @@ -1,10 +1,7 @@ [package] name = "interop" -version = "0.0.0" -authors = ["Stepan Koltsov "] -publish = false -edition = "2018" description = "Test different implementations compatible with each other" +publish = false [lib] bench = false @@ -16,9 +13,11 @@ runtime-tokio = ["tls-api-test/runtime-tokio", "tls-api-openssl/runtime-tokio", [dependencies] tls-api = { path = "../api", default-features = false } -tls-api-test = { path = "../api-test", default-features = false } +tls-api-test = { path = "../api-test", package = "tls-api-test-2", default-features = false } tls-api-openssl = { path = "../impl-openssl", default-features = false } tls-api-rustls = { path = "../impl-rustls", default-features = false } tls-api-native-tls = { path = "../impl-native-tls", default-features = false } tls-api-security-framework = { path = "../impl-security-framework", default-features = false } -test-cert-gen = { path = "../test-cert-gen", default-features = false } +test-cert-gen = { path = "../test-cert-gen", package = "test-cert-gen-2", default-features = false } + +rustls = { version = "0.23.5", default-features = false, features = ["ring", "tls12"] } diff --git a/interop/tests/alpn.rs b/interop/tests/alpn.rs index 430427d..efc23f4 100644 --- a/interop/tests/alpn.rs +++ b/interop/tests/alpn.rs @@ -1,8 +1,4 @@ //! Invoke `tls_api_test::alpn` with various implementations -// Dummy test to help Idea regognise this file is a test -#[test] -fn dummy() {} - // All permutations. include!(concat!(env!("OUT_DIR"), "/alpn.rs")); diff --git a/interop/tests/client_server.rs b/interop/tests/client_server.rs index 1f47559..076e7f9 100644 --- a/interop/tests/client_server.rs +++ b/interop/tests/client_server.rs @@ -1,8 +1,4 @@ //! Invoke `tls_api_test::server` with various implementations -// Dummy test to help Idea regognise this file is a test -#[test] -fn dummy() {} - // All permutations. include!(concat!(env!("OUT_DIR"), "/client_server.rs")); diff --git a/test-cert-gen/Cargo.toml b/test-cert-gen/Cargo.toml index 57c3bad..6ea9f60 100644 --- a/test-cert-gen/Cargo.toml +++ b/test-cert-gen/Cargo.toml @@ -1,15 +1,18 @@ [package] -name = "test-cert-gen" -version = "0.10.0-pre" -authors = ["Stiopa Koltsov "] -edition = "2018" +name = "test-cert-gen-2" description = "Utility to generate certificates for tests (e. g. for TLS)" -license = "MIT/Apache-2.0" + +version = { workspace = true } +authors = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +edition = { workspace = true } +keywords = { workspace = true } [lib] bench = false [dependencies] tempfile = "3.3.0" -pem = "0.8.3" +pem = "3.0.4" once_cell = "1.10.0" diff --git a/test-cert-gen/README.md b/test-cert-gen/README.md index 09ceb5f..c5209e6 100644 --- a/test-cert-gen/README.md +++ b/test-cert-gen/README.md @@ -1,7 +1,3 @@ -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/stepancheg/rust-tls-api/CI)](https://github.com/stepancheg/rust-tls-api/actions?query=workflow%3ACI) -[![License](https://img.shields.io/crates/l/tls-api.svg)](https://github.com/stepancheg/rust-tls-api/blob/master/LICENSE) -[![crates.io](https://img.shields.io/crates/v/tls-api.svg)](https://crates.io/crates/tls-api) - # test-cert-gen Utility to generate certificates for tests. diff --git a/test-cert-gen/src/bin/gen-certs-here.rs b/test-cert-gen/src/bin/gen-certs-here.rs index 85365a4..1033557 100644 --- a/test-cert-gen/src/bin/gen-certs-here.rs +++ b/test-cert-gen/src/bin/gen-certs-here.rs @@ -1,7 +1,7 @@ use std::fs; fn main() { - let keys = test_cert_gen::gen_keys(); + let keys = test_cert_gen_2::gen_keys(); println!("writing server cert to server.pem"); fs::write("server.pem", keys.server.cert_and_key.to_pem_incorrect()).unwrap(); diff --git a/test-cert-gen/src/cert.rs b/test-cert-gen/src/cert.rs index b3f661c..d8d10e8 100644 --- a/test-cert-gen/src/cert.rs +++ b/test-cert-gen/src/cert.rs @@ -26,18 +26,18 @@ impl Cert { } /// Construct from PEM-DER-encoded. - pub fn from_pem(cert_der_pem: impl AsRef<[u8]>) -> Cert { - let pem = pem::parse_many(cert_der_pem.as_ref()); + pub fn from_pem(cert_der_pem: impl AsRef<[u8]>) -> Result { + let pem = pem::parse_many(cert_der_pem.as_ref())?; let count = pem.len(); let mut certs: Vec = pem .into_iter() - .flat_map(|p| match p.tag == "CERTIFICATE" { - true => Some(Self::from_der(p.contents)), + .flat_map(|p| match p.tag() == "CERTIFICATE" { + true => Some(Self::from_der(p.contents())), false => None, }) .collect(); if certs.len() == 1 { - return certs.swap_remove(0); + Ok(certs.swap_remove(0)) } else if certs.len() > 1 { panic!("PEM file contains {} certificates", certs.len()); } else if count != 0 { @@ -59,10 +59,7 @@ impl Cert { /// Convert a certificate to PEM format. pub fn to_pem(&self) -> String { - pem::encode(&pem::Pem { - tag: "CERTIFICATE".to_owned(), - contents: self.0.clone(), - }) + pem::encode(&pem::Pem::new("CERTIFICATE", self.0.to_vec())) } } @@ -89,18 +86,18 @@ impl PrivateKey { /// Construct a private key from PEM text file. /// /// This operation returns an error if PEM file contains zero or more than one certificate. - pub fn from_pem(key_pem: impl AsRef<[u8]>) -> PrivateKey { - let pem = pem::parse_many(key_pem.as_ref()); + pub fn from_pem(key_pem: impl AsRef<[u8]>) -> Result { + let pem = pem::parse_many(key_pem.as_ref())?; let count = pem.len(); let mut keys: Vec = pem .into_iter() - .flat_map(|p| match p.tag.as_ref() { - "PRIVATE KEY" | "RSA PRIVATE KEY" => Some(Self::from_der(p.contents)), + .flat_map(|p| match p.tag() { + "PRIVATE KEY" | "RSA PRIVATE KEY" => Some(Self::from_der(p.contents())), _ => None, }) .collect(); if keys.len() == 1 { - return keys.swap_remove(0); + Ok(keys.swap_remove(0)) } else if keys.len() > 1 { panic!("PEM file contains {} private keys", keys.len()); } else if count != 0 { @@ -121,25 +118,22 @@ impl PrivateKey { /// without verifying that the private key is actually RSA. #[doc(hidden)] pub fn to_pem_incorrect(&self) -> String { - pem::encode(&pem::Pem { - tag: "RSA PRIVATE KEY".to_owned(), - contents: self.0.clone(), - }) + pem::encode(&pem::Pem::new("RSA PRIVATE KEY", self.0.clone())) } } /// Parse PEM file into a pair of certificate and private key. -pub fn pem_to_cert_key_pair(pem: &[u8]) -> (Cert, PrivateKey) { - let entries = pem::parse_many(pem); +pub fn pem_to_cert_key_pair(pem: &[u8]) -> Result<(Cert, PrivateKey), pem::PemError> { + let entries = pem::parse_many(pem)?; if entries.len() != 2 { panic!( "PEM file should contain certificate and private key entries, got {} entries", entries.len() ); } - let cert = Cert::from_pem(pem); - let key = PrivateKey::from_pem(pem); - (cert, key) + let cert = Cert::from_pem(pem)?; + let key = PrivateKey::from_pem(pem)?; + Ok((cert, key)) } /// DER-encoded diff --git a/test-cert-gen/src/lib.rs b/test-cert-gen/src/lib.rs index af99da9..81ac7a9 100644 --- a/test-cert-gen/src/lib.rs +++ b/test-cert-gen/src/lib.rs @@ -107,12 +107,12 @@ fn gen_root_ca() -> CertAndPrivateKey { let cert = fs::read_to_string(&certfile).unwrap(); let key = fs::read_to_string(&keyfile).unwrap(); - assert_eq!(1, pem::parse_many(cert.as_bytes()).len()); - assert_eq!(1, pem::parse_many(key.as_bytes()).len()); + assert_eq!(1, pem::parse_many(cert.as_bytes()).unwrap().len()); + assert_eq!(1, pem::parse_many(key.as_bytes()).unwrap().len()); CertAndPrivateKey { - cert: Cert::from_pem(&cert), - key: PrivateKey::from_pem(&key), + cert: Cert::from_pem(&cert).unwrap(), + key: PrivateKey::from_pem(&key).unwrap(), } } @@ -220,12 +220,12 @@ fn gen_cert_for_domain(domain: &str, ca: &CertAndPrivateKey) -> CertAndPrivateKe let cert = fs::read_to_string(&cert_path).unwrap(); // verify - assert_eq!(1, pem::parse_many(cert.as_bytes()).len()); - assert_eq!(1, pem::parse_many(key.as_bytes()).len()); + assert_eq!(1, pem::parse_many(cert.as_bytes()).unwrap().len()); + assert_eq!(1, pem::parse_many(key.as_bytes()).unwrap().len()); CertAndPrivateKey { - cert: Cert::from_pem(&cert), - key: PrivateKey::from_pem(&key), + cert: Cert::from_pem(&cert).unwrap(), + key: PrivateKey::from_pem(&key).unwrap(), } } @@ -249,7 +249,7 @@ pub fn gen_keys() -> Keys { /// Generate keys pub fn keys() -> &'static Keys { - static KEYS: Lazy = Lazy::new(|| gen_keys()); + static KEYS: Lazy = Lazy::new(gen_keys); &KEYS } @@ -346,7 +346,7 @@ mod test { let server_pem = temp_dir.path().join("server.pem"); fs::write(&ca_pem, keys.client.ca.to_pem()).unwrap(); - fs::write(&server_pem, &keys.server.cert_and_key.to_pem_incorrect()).unwrap(); + fs::write(&server_pem, keys.server.cert_and_key.to_pem_incorrect()).unwrap(); // error is, what does it mean? // ``` @@ -378,7 +378,7 @@ mod test { let client = temp_dir.path().join("client"); let server = temp_dir.path().join("server.pem"); - fs::write(&client, keys.client.ca.get_der()).unwrap(); + fs::write(client, keys.client.ca.get_der()).unwrap(); fs::write(&server, keys.server.cert_and_key.to_pem_incorrect()).unwrap(); let port = 1234;