update rustls and related dependencies (#72)

* chore(deps): bump rustls from 0.19.1 to 0.20.0
* chore(deps): bump webpki from 0.21.4 to 0.22.0
* chore(deps): bump tokio-rustls from 0.22.0 to 0.23.0
* update rustls calls
* update webpki naming
* update tests
This commit is contained in:
Johann150 2021-10-19 00:30:38 +02:00
parent 824d384693
commit b5a416fc35
No known key found for this signature in database
GPG key ID: 9EE6577A2A06F8F1
5 changed files with 121 additions and 80 deletions

65
Cargo.lock generated
View file

@ -18,11 +18,11 @@ dependencies = [
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"rcgen", "rcgen",
"rustls", "rustls 0.20.0",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls 0.23.0",
"url", "url",
"webpki", "webpki 0.22.0",
] ]
[[package]] [[package]]
@ -166,12 +166,12 @@ checksum = "aa12dfaa57be769c6681b4d193398cae8db7f7b9af3e86d362d7f0a3c294a1a0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"ring", "ring",
"rustls", "rustls 0.19.1",
"thiserror", "thiserror",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls 0.22.0",
"url", "url",
"webpki", "webpki 0.21.4",
"webpki-roots", "webpki-roots",
"x509-signature", "x509-signature",
] ]
@ -451,8 +451,20 @@ dependencies = [
"base64", "base64",
"log", "log",
"ring", "ring",
"sct", "sct 0.6.1",
"webpki", "webpki 0.21.4",
]
[[package]]
name = "rustls"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b5ac6078ca424dc1d3ae2328526a76787fecc7f8011f520e3276730e711fc95"
dependencies = [
"log",
"ring",
"sct 0.7.0",
"webpki 0.22.0",
] ]
[[package]] [[package]]
@ -465,6 +477,16 @@ dependencies = [
"untrusted", "untrusted",
] ]
[[package]]
name = "sct"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
dependencies = [
"ring",
"untrusted",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.5" version = "0.4.5"
@ -566,9 +588,20 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
dependencies = [ dependencies = [
"rustls", "rustls 0.19.1",
"tokio", "tokio",
"webpki", "webpki 0.21.4",
]
[[package]]
name = "tokio-rustls"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d49194a46b06a69f2498a34a595ab4a9c1babd2642ffa3dbccf6c6778d1426f2"
dependencies = [
"rustls 0.20.0",
"tokio",
"webpki 0.22.0",
] ]
[[package]] [[package]]
@ -705,13 +738,23 @@ dependencies = [
"untrusted", "untrusted",
] ]
[[package]]
name = "webpki"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
dependencies = [
"ring",
"untrusted",
]
[[package]] [[package]]
name = "webpki-roots" name = "webpki-roots"
version = "0.21.1" version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
dependencies = [ dependencies = [
"webpki", "webpki 0.21.4",
] ]
[[package]] [[package]]

View file

@ -22,11 +22,11 @@ mime_guess = "2.0"
once_cell = "1.5" once_cell = "1.5"
percent-encoding = "2.1" percent-encoding = "2.1"
rcgen = { version = "0.8.14" } rcgen = { version = "0.8.14" }
rustls = "0.19.0" rustls = "0.20.0"
tokio-rustls = "0.22.0" tokio-rustls = "0.23.0"
tokio = { version = "1.2", features = ["fs", "io-util", "net", "rt-multi-thread", "sync"] } tokio = { version = "1.2", features = ["fs", "io-util", "net", "rt-multi-thread", "sync"] }
url = "2.2.1" url = "2.2.1"
webpki = "0.21.4" webpki = "0.22.0"
[dev-dependencies] [dev-dependencies]
anyhow = "1.0" anyhow = "1.0"

View file

@ -1,7 +1,7 @@
use { use {
rustls::{ rustls::{
sign::{any_supported_type, CertifiedKey}, server::{ClientHello, ResolvesServerCert},
ResolvesServerCert, sign::{any_supported_type, CertifiedKey, SignError},
}, },
std::{ std::{
ffi::OsStr, ffi::OsStr,
@ -9,7 +9,6 @@ use {
path::Path, path::Path,
sync::Arc, sync::Arc,
}, },
webpki::DNSNameRef,
}; };
/// A struct that holds all loaded certificates and the respective domain /// A struct that holds all loaded certificates and the respective domain
@ -17,7 +16,7 @@ use {
pub(crate) struct CertStore { pub(crate) struct CertStore {
/// Stores the certificates and the domains they apply to, sorted by domain /// Stores the certificates and the domains they apply to, sorted by domain
/// names, longest matches first /// names, longest matches first
certs: Vec<(String, CertifiedKey)>, certs: Vec<(String, Arc<CertifiedKey>)>,
} }
pub static CERT_FILE_NAME: &str = "cert.der"; pub static CERT_FILE_NAME: &str = "cert.der";
@ -29,14 +28,9 @@ pub enum CertLoadError {
NoReadCertDir, NoReadCertDir,
/// no certificates or keys were found /// no certificates or keys were found
Empty, Empty,
/// the specified domain name cannot be processed correctly
BadDomain(String),
/// the key file for the specified domain is bad (e.g. does not contain a /// the key file for the specified domain is bad (e.g. does not contain a
/// key or is invalid) /// key or is invalid)
BadKey(String), BadKey(String, SignError),
/// The certificate file for the specified domain is bad (e.g. invalid)
/// The second parameter is the error message.
BadCert(String, String),
/// the key file for the specified domain is missing (but a certificate /// the key file for the specified domain is missing (but a certificate
/// file was present) /// file was present)
MissingKey(String), MissingKey(String),
@ -53,16 +47,7 @@ impl Display for CertLoadError {
match self { match self {
Self::NoReadCertDir => write!(f, "Could not read from certificate directory."), Self::NoReadCertDir => write!(f, "Could not read from certificate directory."),
Self::Empty => write!(f, "No keys or certificates were found in the given directory.\nSpecify the --hostname option to generate these automatically."), Self::Empty => write!(f, "No keys or certificates were found in the given directory.\nSpecify the --hostname option to generate these automatically."),
Self::BadDomain(domain) if !domain.is_ascii() => write!( Self::BadKey(domain, err) => write!(f, "The key file for {} is malformed: {:?}", domain, err),
f,
"The domain name {} cannot be processed, it must be punycoded.",
domain
),
Self::BadDomain(domain) => write!(f, "The domain name {} cannot be processed.", domain),
Self::BadKey(domain) => write!(f, "The key file for {} is malformed.", domain),
Self::BadCert(domain, e) => {
write!(f, "The certificate file for {} is malformed: {}", domain, e)
}
Self::MissingKey(domain) => write!(f, "The key file for {} is missing.", domain), Self::MissingKey(domain) => write!(f, "The key file for {} is missing.", domain),
Self::MissingCert(domain) => { Self::MissingCert(domain) => {
write!(f, "The certificate file for {} is missing.", domain) write!(f, "The certificate file for {} is missing.", domain)
@ -106,9 +91,9 @@ fn load_domain(certs_dir: &Path, domain: String) -> Result<CertifiedKey, CertLoa
// transform key to correct format // transform key to correct format
let key = match any_supported_type(&key) { let key = match any_supported_type(&key) {
Ok(key) => key, Ok(key) => key,
Err(()) => return Err(CertLoadError::BadKey(domain)), Err(e) => return Err(CertLoadError::BadKey(domain, e)),
}; };
Ok(CertifiedKey::new(vec![cert], Arc::new(key))) Ok(CertifiedKey::new(vec![cert], key))
} }
impl CertStore { impl CertStore {
@ -127,14 +112,9 @@ impl CertStore {
// certificate directory. // certificate directory.
match load_domain(certs_dir, String::new()) { match load_domain(certs_dir, String::new()) {
Err(CertLoadError::EmptyDomain(_)) => { /* there are no fallback keys */ } Err(CertLoadError::EmptyDomain(_)) => { /* there are no fallback keys */ }
Err(CertLoadError::Empty) Err(CertLoadError::Empty) | Err(CertLoadError::NoReadCertDir) => unreachable!(),
| Err(CertLoadError::NoReadCertDir) Err(CertLoadError::BadKey(_, e)) => {
| Err(CertLoadError::BadDomain(_)) => unreachable!(), return Err(CertLoadError::BadKey("fallback".to_string(), e))
Err(CertLoadError::BadKey(_)) => {
return Err(CertLoadError::BadKey("fallback".to_string()))
}
Err(CertLoadError::BadCert(_, e)) => {
return Err(CertLoadError::BadCert("fallback".to_string(), e))
} }
Err(CertLoadError::MissingKey(_)) => { Err(CertLoadError::MissingKey(_)) => {
return Err(CertLoadError::MissingKey("fallback".to_string())) return Err(CertLoadError::MissingKey("fallback".to_string()))
@ -145,7 +125,7 @@ impl CertStore {
// For the fallback keys there is no domain name to verify them // For the fallback keys there is no domain name to verify them
// against, so we can skip that step and only have to do it for the // against, so we can skip that step and only have to do it for the
// other keys below. // other keys below.
Ok(key) => certs.push((String::new(), key)), Ok(key) => certs.push((String::new(), Arc::new(key))),
} }
for file in certs_dir for file in certs_dir
@ -163,16 +143,9 @@ impl CertStore {
.unwrap() .unwrap()
.to_string(); .to_string();
let dns_name = match DNSNameRef::try_from_ascii_str(&filename) {
Ok(name) => name,
Err(_) => return Err(CertLoadError::BadDomain(filename)),
};
let key = load_domain(certs_dir, filename.clone())?; let key = load_domain(certs_dir, filename.clone())?;
key.cross_check_end_entity_cert(Some(dns_name))
.map_err(|e| CertLoadError::BadCert(filename.clone(), e.to_string()))?;
certs.push((filename, key)); certs.push((filename, Arc::new(key)));
} }
if certs.is_empty() { if certs.is_empty() {
@ -211,9 +184,9 @@ impl CertStore {
} }
impl ResolvesServerCert for CertStore { impl ResolvesServerCert for CertStore {
fn resolve(&self, client_hello: rustls::ClientHello<'_>) -> Option<CertifiedKey> { fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
if let Some(name) = client_hello.server_name() { if let Some(name) = client_hello.server_name() {
let name: &str = name.into(); let name: &str = name;
// The certificate list is sorted so the longest match will always // The certificate list is sorted so the longest match will always
// appear first. We have to find the first that is either this // appear first. We have to find the first that is either this
// domain or a parent domain of the current one. // domain or a parent domain of the current one.

View file

@ -8,7 +8,7 @@ use {
once_cell::sync::Lazy, once_cell::sync::Lazy,
percent_encoding::{percent_decode_str, percent_encode, AsciiSet, CONTROLS}, percent_encoding::{percent_decode_str, percent_encode, AsciiSet, CONTROLS},
rcgen::{Certificate, CertificateParams, DnType}, rcgen::{Certificate, CertificateParams, DnType},
rustls::{NoClientAuth, ServerConfig}, rustls::server::ServerConfig,
std::{ std::{
borrow::Cow, borrow::Cow,
error::Error, error::Error,
@ -318,11 +318,17 @@ fn check_path(s: String) -> Result<PathBuf, String> {
static TLS: Lazy<TlsAcceptor> = Lazy::new(acceptor); static TLS: Lazy<TlsAcceptor> = Lazy::new(acceptor);
fn acceptor() -> TlsAcceptor { fn acceptor() -> TlsAcceptor {
let mut config = ServerConfig::new(NoClientAuth::new()); let config = if ARGS.only_tls13 {
if ARGS.only_tls13 { ServerConfig::builder()
config.versions = vec![rustls::ProtocolVersion::TLSv1_3]; .with_safe_default_cipher_suites()
.with_safe_default_kx_groups()
.with_protocol_versions(&[&rustls::version::TLS13])
.expect("could not build server config")
} else {
ServerConfig::builder().with_safe_defaults()
} }
config.cert_resolver = ARGS.certs.clone(); .with_no_client_auth()
.with_cert_resolver(ARGS.certs.clone());
TlsAcceptor::from(Arc::new(config)) TlsAcceptor::from(Arc::new(config))
} }

View file

@ -1,5 +1,6 @@
use anyhow::anyhow; use anyhow::anyhow;
use gemini_fetch::{Header, Page, Status}; use gemini_fetch::{Header, Page, Status};
use std::convert::TryInto;
use std::io::{BufRead, BufReader, Read}; use std::io::{BufRead, BufReader, Read};
use std::net::{SocketAddr, ToSocketAddrs}; use std::net::{SocketAddr, ToSocketAddrs};
use std::path::PathBuf; use std::path::PathBuf;
@ -469,18 +470,24 @@ fn directory_traversal_regression() {
/// (lower versions do not have to be tested because rustls does not even /// (lower versions do not have to be tested because rustls does not even
/// support them, making agate incapable of accepting them) /// support them, making agate incapable of accepting them)
fn explicit_tls_version() { fn explicit_tls_version() {
use rustls::{ClientSession, ProtocolVersion, TLSError}; use rustls::{ClientConnection, Error, RootCertStore};
use std::io::Read; use std::io::Read;
use std::net::TcpStream; use std::net::TcpStream;
let _server = Server::new(&["--addr", "[::]:1976", "-3"]); let _server = Server::new(&["--addr", "[::]:1976", "-3"]);
let mut config = rustls::ClientConfig::new(); let config = rustls::ClientConfig::builder()
// try to connect using only TLS 1.2 .with_safe_default_cipher_suites()
config.versions = vec![ProtocolVersion::TLSv1_2]; .with_safe_default_kx_groups()
// try to connect using only TLS 1.2
.with_protocol_versions(&[&rustls::version::TLS12])
.unwrap()
.with_root_certificates(RootCertStore::empty())
.with_no_client_auth();
let dns_name = webpki::DNSNameRef::try_from_ascii_str("localhost").unwrap(); let mut session =
let mut session = ClientSession::new(&std::sync::Arc::new(config), dns_name); ClientConnection::new(std::sync::Arc::new(config), "localhost".try_into().unwrap())
.unwrap();
let mut tcp = TcpStream::connect(addr(1976)).unwrap(); let mut tcp = TcpStream::connect(addr(1976)).unwrap();
let mut tls = rustls::Stream::new(&mut session, &mut tcp); let mut tls = rustls::Stream::new(&mut session, &mut tcp);
@ -490,9 +497,9 @@ fn explicit_tls_version() {
.unwrap_err() .unwrap_err()
.into_inner() .into_inner()
.unwrap() .unwrap()
.downcast::<TLSError>() .downcast::<Error>()
.unwrap(), .unwrap(),
TLSError::AlertReceived(rustls::internal::msgs::enums::AlertDescription::ProtocolVersion) Error::AlertReceived(rustls::internal::msgs::enums::AlertDescription::ProtocolVersion)
) )
} }
@ -586,15 +593,14 @@ mod multicert {
#[test] #[test]
fn example_com() { fn example_com() {
use rustls::{Certificate, ClientSession}; use rustls::{Certificate, ClientConnection, RootCertStore};
use std::io::Write; use std::io::Write;
use std::net::TcpStream; use std::net::TcpStream;
let mut server = Server::new(&["--addr", "[::]:1981", "--certs", "multicert"]); let mut server = Server::new(&["--addr", "[::]:1981", "--certs", "multicert"]);
let mut config = rustls::ClientConfig::new(); let mut certs = RootCertStore::empty();
config certs
.root_store
.add(&Certificate( .add(&Certificate(
include_bytes!(concat!( include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"), env!("CARGO_MANIFEST_DIR"),
@ -603,9 +609,16 @@ mod multicert {
.to_vec(), .to_vec(),
)) ))
.unwrap(); .unwrap();
let config = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(certs)
.with_no_client_auth();
let dns_name = webpki::DNSNameRef::try_from_ascii_str("example.com").unwrap(); let mut session = ClientConnection::new(
let mut session = ClientSession::new(&std::sync::Arc::new(config), dns_name); std::sync::Arc::new(config),
"example.com".try_into().unwrap(),
)
.unwrap();
let mut tcp = TcpStream::connect(addr(1981)).unwrap(); let mut tcp = TcpStream::connect(addr(1981)).unwrap();
let mut tls = rustls::Stream::new(&mut session, &mut tcp); let mut tls = rustls::Stream::new(&mut session, &mut tcp);
@ -619,15 +632,14 @@ mod multicert {
#[test] #[test]
fn example_org() { fn example_org() {
use rustls::{Certificate, ClientSession}; use rustls::{Certificate, ClientConnection, RootCertStore};
use std::io::Write; use std::io::Write;
use std::net::TcpStream; use std::net::TcpStream;
let mut server = Server::new(&["--addr", "[::]:1982", "--certs", "multicert"]); let mut server = Server::new(&["--addr", "[::]:1982", "--certs", "multicert"]);
let mut config = rustls::ClientConfig::new(); let mut certs = RootCertStore::empty();
config certs
.root_store
.add(&Certificate( .add(&Certificate(
include_bytes!(concat!( include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"), env!("CARGO_MANIFEST_DIR"),
@ -636,9 +648,16 @@ mod multicert {
.to_vec(), .to_vec(),
)) ))
.unwrap(); .unwrap();
let config = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(certs)
.with_no_client_auth();
let dns_name = webpki::DNSNameRef::try_from_ascii_str("example.org").unwrap(); let mut session = ClientConnection::new(
let mut session = ClientSession::new(&std::sync::Arc::new(config), dns_name); std::sync::Arc::new(config),
"example.org".try_into().unwrap(),
)
.unwrap();
let mut tcp = TcpStream::connect(addr(1982)).unwrap(); let mut tcp = TcpStream::connect(addr(1982)).unwrap();
let mut tls = rustls::Stream::new(&mut session, &mut tcp); let mut tls = rustls::Stream::new(&mut session, &mut tcp);