From 6267f4c777d0644b09d16d2c38b485768880f79b Mon Sep 17 00:00:00 2001 From: TheDaemoness Date: Thu, 25 Apr 2024 14:43:39 -0700 Subject: [PATCH] Add crypto feature, update to rustls 0.23 rustls 0.22 adds support for alternate crypto providers; 0.23 defaults to aws_lc_rs. The tls feature now pulls in NO crypto providers, but expects a process-default crypto provider to be set before doing TLS. The crypto feature pulls in ring and adds it as a provider for rustls, as we'll be using ring anyway for fancier SASL auths. Due to the 78-column limit being cumbersome on Cargo.toml, it has been restricted to only apply to markdown files. --- .editorconfig | 2 + Cargo.toml | 12 ++--- doc/rustdoc/lib.md | 4 ++ src/client/conn/sync.rs | 4 +- src/client/conn/tokio.rs | 8 ++-- src/client/tls.rs | 96 ++++++++++++++++++++++++++++++---------- 6 files changed, 91 insertions(+), 35 deletions(-) diff --git a/.editorconfig b/.editorconfig index c13ebda..1c400e5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,6 +3,8 @@ charset = utf-8 insert_final_newline = true indent_style = space indent_size = 2 + +[*.md] max_line_length = 78 [*.rs] diff --git a/Cargo.toml b/Cargo.toml index e4631d1..a1cde96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,19 +17,21 @@ include = ["/src", "/doc/rustdoc/*", "/README.md"] [dependencies] base64 = { version = "0.21.2", optional = true } -rustls = { version = "0.21.4", optional = true, features = ["dangerous_configuration"] } -rustls-native-certs = { version = "0.6.3", optional = true } -rustls-pemfile = { version = "1.0.2", optional = true } +ring = { version = "0.17.8", optional = true } +rustls = { version = "0.23.5", optional = true, default-features = false, features = ["std", "tls12"] } +rustls-native-certs = { version = "0.7.0", optional = true } +rustls-pemfile = { version = "2.1.2", optional = true } serde = { version = "1.0", features = ["rc"], optional = true } serde_derive = { version = ">= 1.0.184", optional = true } tokio = { version = "1.28.2", features = ["io-util", "net", "time", "rt", "sync"], optional = true } -tokio-rustls = { version = "0.24.1", optional = true } +tokio-rustls = { version = "0.26.0", optional = true, default-features = false} tracing = { version = "0.1.37", default-features = false, features = ["std"], optional = true } whoami = { version = "1.5.0", optional = true } [features] -default = ["base64", "client", "tls-tokio"] +default = ["base64", "client", "crypto", "tls-tokio"] client = [] +crypto = ["dep:ring", "rustls?/ring"] serde = ["dep:serde", "dep:serde_derive"] tls = ["dep:rustls", "dep:rustls-native-certs", "dep:rustls-pemfile"] tls-tokio = ["dep:tokio-rustls", "tls", "tokio"] diff --git a/doc/rustdoc/lib.md b/doc/rustdoc/lib.md index c7f3516..89a0342 100644 --- a/doc/rustdoc/lib.md +++ b/doc/rustdoc/lib.md @@ -19,8 +19,12 @@ and includes the following: Adds base64 encoding/decoding. * `client`: Adds utilities for building client-side IRC software. +* `crypto`: +Adds `ring`-based cryptography, required for some SASL authenticators. +Also adds `ring` as a crypto provider to rustls if `tls` is enabled. * `tls`: Adds utilities for working with rustls. +Does NOT pull in any crypto providers. * `tls-tokio`: Implies `tls` and `tokio`. Adds support for asynchronous TLS connections. * `tokio`: diff --git a/src/client/conn/sync.rs b/src/client/conn/sync.rs index 17bfff6..18ce9e4 100644 --- a/src/client/conn/sync.rs +++ b/src/client/conn/sync.rs @@ -26,10 +26,10 @@ impl<'a> super::ServerAddr<'a> { use std::io::{Error, ErrorKind}; let string = self.utf8_address()?; let stream = if self.tls { - let name = rustls::ServerName::try_from(string) + let name = rustls::pki_types::ServerName::try_from(string) .map_err(|e| Error::new(ErrorKind::InvalidInput, e))?; let config = tls_fn()?; - let conn = rustls::ClientConnection::new(config, name) + let conn = rustls::ClientConnection::new(config, name.to_owned()) .map_err(|e| Error::new(ErrorKind::Other, e))?; let sock = std::net::TcpStream::connect((string, self.port_num()))?; let mut tls = rustls::StreamOwned { conn, sock }; diff --git a/src/client/conn/tokio.rs b/src/client/conn/tokio.rs index 3393a9b..5ed2c4e 100644 --- a/src/client/conn/tokio.rs +++ b/src/client/conn/tokio.rs @@ -1,5 +1,5 @@ use super::{timed_io, Bidir, TimeLimitedTokio}; -use crate::{client::tls::TlsConfig, ircmsg::ServerMsg}; +use crate::ircmsg::ServerMsg; use std::{pin::Pin, time::Duration}; use tokio::{ io::{AsyncBufRead, AsyncWrite, BufReader}, @@ -21,17 +21,17 @@ impl<'a> super::ServerAddr<'a> { #[cfg(feature = "tls-tokio")] pub async fn connect_tokio( &self, - tls_fn: impl FnOnce() -> std::io::Result, + tls_fn: impl FnOnce() -> std::io::Result, ) -> std::io::Result> { use std::io::{Error, ErrorKind}; let string = self.utf8_address()?; let stream = if self.tls { - let name = rustls::ServerName::try_from(string) + let name = rustls::pki_types::ServerName::try_from(string) .map_err(|e| Error::new(ErrorKind::InvalidInput, e))?; let config = tls_fn()?; let conn: tokio_rustls::TlsConnector = config.into(); let sock = tokio::net::TcpStream::connect((string, self.port_num())).await?; - let tls = conn.connect(name, sock).await?; + let tls = conn.connect(name.to_owned(), sock).await?; StreamInner::Tls(tls) } else { let sock = tokio::net::TcpStream::connect((string, self.port_num())).await?; diff --git a/src/client/tls.rs b/src/client/tls.rs index e1288fb..8296ab6 100644 --- a/src/client/tls.rs +++ b/src/client/tls.rs @@ -1,6 +1,9 @@ //! Helpers for creating TLS connections. -use rustls::{Certificate, ClientConfig, RootCertStore}; +use rustls::{ + pki_types::{CertificateDer, PrivateKeyDer}, + ClientConfig, RootCertStore, +}; use std::{ path::{Path, PathBuf}, sync::Arc, @@ -24,22 +27,62 @@ pub enum Trust { NoVerify, } -/// `ServerCertVerifier` that verifies literally everything. -#[derive(Clone, Copy, Debug, Default)] -struct NoVerifier; +/// `ServerCertVerifier` that doesn't care at all about the server cert. +#[derive(Clone, Copy, Debug)] +struct NoVerifier(&'static Arc); -impl rustls::client::ServerCertVerifier for NoVerifier { +impl Default for NoVerifier { + fn default() -> Self { + NoVerifier( + rustls::crypto::CryptoProvider::get_default() + .expect("no default rustls crypto prodiver"), + ) + } +} + +impl rustls::client::danger::ServerCertVerifier for NoVerifier { fn verify_server_cert( &self, - _: &rustls::Certificate, - _: &[rustls::Certificate], - _: &rustls::ServerName, - _: &mut dyn Iterator, + _: &CertificateDer<'_>, + _: &[CertificateDer<'_>], + _: &rustls::pki_types::ServerName<'_>, _: &[u8], - _: std::time::SystemTime, - ) -> Result { + _: rustls::pki_types::UnixTime, + ) -> Result { // :) - Ok(rustls::client::ServerCertVerified::assertion()) + Ok(rustls::client::danger::ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + rustls::crypto::verify_tls12_signature( + message, + cert, + dss, + &self.0.signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + rustls::crypto::verify_tls12_signature( + message, + cert, + dss, + &self.0.signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + self.0.signature_verification_algorithms.supported_schemes() } } @@ -61,25 +104,28 @@ pub struct TlsConfigOptions { fn load_pem(path: &Path, certs: &mut RootCertStore) -> std::io::Result<()> { let mut file = std::io::BufReader::new(std::fs::File::open(path)?); - for cert in rustls_pemfile::certs(&mut file)? { + for cert in rustls_pemfile::certs(&mut file) { + let cert = cert?; certs - .add(&rustls::Certificate(cert)) + .add(CertificateDer::from(cert)) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; } Ok(()) } -fn load_client_cert(path: &Path) -> std::io::Result<(Vec, rustls::PrivateKey)> { - let mut key = Option::::None; - let mut certs = Vec::::new(); +fn load_client_cert( + path: &Path, +) -> std::io::Result<(Vec>, PrivateKeyDer<'static>)> { + let mut key = Option::::None; + let mut certs = Vec::::new(); let mut file = std::io::BufReader::new(std::fs::File::open(path)?); while let Some(item) = rustls_pemfile::read_one(&mut file)? { match item { rustls_pemfile::Item::X509Certificate(c) => { - certs.push(Certificate(c)); + certs.push(CertificateDer::from(c)); } - rustls_pemfile::Item::PKCS8Key(k) => { - key = Some(rustls::PrivateKey(k)); + rustls_pemfile::Item::Pkcs8Key(k) => { + key = Some(PrivateKeyDer::from(k)); } _ => (), } @@ -94,10 +140,12 @@ impl TlsConfigOptions { /// This is an expensive operation. It should ideally be done only once per network. pub fn build(&self) -> std::io::Result { let cli_auth = - if let Some(path) = &self.cert { Some(load_client_cert(path)?) } else { None }; - let builder = ClientConfig::builder().with_safe_defaults(); + if let Some(path) = self.cert.as_ref() { Some(load_client_cert(path)?) } else { None }; + let builder = ClientConfig::builder(); let config = if matches!(&self.trust, Trust::NoVerify) { - let builder = builder.with_custom_certificate_verifier(Arc::new(NoVerifier)); + let builder = builder + .dangerous() + .with_custom_certificate_verifier(Arc::new(NoVerifier::default())); if let Some((certs, key)) = cli_auth { builder .with_client_auth_cert(certs, key) @@ -109,7 +157,7 @@ impl TlsConfigOptions { let mut certs = RootCertStore { roots: Vec::new() }; if matches!(&self.trust, Trust::Default | Trust::Also(_)) { let natives = rustls_native_certs::load_native_certs()?; - certs.add_parsable_certificates(&natives); + certs.add_parsable_certificates(natives); } if let Trust::Only(paths) | Trust::Also(paths) = &self.trust { certs.roots.reserve_exact(paths.len());