Skip to content

Commit

Permalink
Merge pull request #336 from RAvenGEr/rumqttc_rustls_0.20
Browse files Browse the repository at this point in the history
rumqttc: update rustls to 0.20
  • Loading branch information
Devdutt Shenoi authored Feb 10, 2022
2 parents 6406090 + d44f219 commit e0ee2ae
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 36 deletions.
13 changes: 7 additions & 6 deletions rumqttc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ websocket = ["async-tungstenite", "ws_stream_tungstenite"]
[dependencies]
tokio = { version = "1.0", features = ["rt", "macros", "io-util", "net", "time"] }
bytes = "1.0"
webpki = "0.21"
tokio-rustls = "0.22"
async-tungstenite = { version = "0.13.1", default-features = false, features = ["tokio-rustls"], optional = true }
ws_stream_tungstenite = { version = "0.6.1", default-features = false, features = ["tokio_io"], optional = true }
webpki = "0.22.0"
tokio-rustls = "0.23.2"
rustls-pemfile = "0.3.0"
async-tungstenite = { version = "0.16.1", default-features = false, features = ["tokio-rustls-native-certs"], optional = true }
ws_stream_tungstenite = { version = "0.7.0", default-features = false, features = ["tokio_io"], optional = true }
mqttbytes = { path = "../mqttbytes", version = "0.6" }
pollster = "0.2"
async-channel = "1.5"
Expand All @@ -40,5 +41,5 @@ envy = "0.4"
jsonwebtoken = "7"
tokio = { version = "1.0", features = ["full", "macros"] }
matches = "0.1.8"
rustls = "0.19"
rustls-native-certs = "0.5.0"
rustls = "0.20.2"
rustls-native-certs = "0.6.1"
14 changes: 10 additions & 4 deletions rumqttc/examples/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ async fn main() -> Result<(), Box<dyn Error>> {
mqtt_options.set_keep_alive(std::time::Duration::from_secs(5));
mqtt_options.set_credentials("username", "password");

// To customise TLS configuration we create a rustls ClientConfig and set it up how we want.
let mut client_config = ClientConfig::new();
// Use rustls-native-certs to load root certificates from the operating system.
client_config.root_store =
rustls_native_certs::load_native_certs().expect("Failed to load platform certificates.");
let mut root_cert_store = rustls::RootCertStore::empty();
for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") {
root_cert_store.add(&rustls::Certificate(cert.0))?;
}

let client_config = ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_cert_store)
.with_no_client_auth();

mqtt_options.set_transport(Transport::tls_with_config(client_config.into()));

let (_client, mut eventloop) = AsyncClient::new(mqtt_options, 10);
Expand Down
1 change: 0 additions & 1 deletion rumqttc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ pub use eventloop::{ConnectionError, Event, EventLoop};
pub use mqttbytes::v4::*;
pub use mqttbytes::*;
pub use state::{MqttState, StateError};
pub use tokio_rustls::rustls::internal::pemfile::{certs, pkcs8_private_keys, rsa_private_keys};
pub use tokio_rustls::rustls::ClientConfig;
pub use tls::Error;

Expand Down
73 changes: 48 additions & 25 deletions rumqttc/src/tls.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use tokio::net::TcpStream;
use tokio_rustls::rustls::internal::pemfile::{certs, pkcs8_private_keys, rsa_private_keys};
use tokio_rustls::rustls::{ClientConfig, TLSError};
use tokio_rustls::webpki::{self, DNSNameRef, InvalidDNSNameError};
use tokio_rustls::rustls;
use tokio_rustls::rustls::client::InvalidDnsNameError;
use tokio_rustls::rustls::{
Certificate, ClientConfig, OwnedTrustAnchor, PrivateKey, RootCertStore, ServerName,
};
use tokio_rustls::webpki;
use tokio_rustls::{client::TlsStream, TlsConnector};

use crate::{Key, MqttOptions, TlsConfiguration};

use std::convert::TryFrom;
use std::io;
use std::io::{BufReader, Cursor};
use std::net::AddrParseError;
Expand All @@ -20,9 +24,9 @@ pub enum Error {
#[error("Web Pki")]
WebPki(#[from] webpki::Error),
#[error("DNS name")]
DNSName(#[from] InvalidDNSNameError),
DNSName(#[from] InvalidDnsNameError),
#[error("TLS error")]
TLS(#[from] TLSError),
TLS(#[from] rustls::Error),
#[error("No valid cert in chain")]
NoValidCertInChain,
}
Expand All @@ -41,30 +45,45 @@ pub async fn tls_connector(tls_config: &TlsConfiguration) -> Result<TlsConnector
alpn,
client_auth,
} => {
let mut config = ClientConfig::new();

// Add ca to root store if the connection is TLS
// NOTE: Adding DER file isn't feasible as some of the chain information
// is lost while converting from pem to der. This method iterates through all the
// certs in the chain, converts each to der and adds them to root store
// TODO: Check if there is a better way to do this
if config
.root_store
.add_pem_file(&mut BufReader::new(Cursor::new(ca)))?
.0
== 0
{
let mut root_cert_store = RootCertStore::empty();
let certs = rustls_pemfile::certs(&mut BufReader::new(Cursor::new(ca)))?;

let trust_anchors = certs.iter().map_while(|cert| {
if let Ok(ta) = webpki::TrustAnchor::try_from_cert_der(&cert[..]) {
Some(OwnedTrustAnchor::from_subject_spki_name_constraints(
ta.subject,
ta.spki,
ta.name_constraints,
))
} else {
None
}
});

root_cert_store.add_server_trust_anchors(trust_anchors);

if root_cert_store.is_empty() {
return Err(Error::NoValidCertInChain);
}

let config = ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_cert_store);

// Add der encoded client cert and key
if let Some(client) = client_auth.as_ref() {
let certs = certs(&mut BufReader::new(Cursor::new(client.0.clone())))?;
let mut config = if let Some(client) = client_auth.as_ref() {
let certs =
rustls_pemfile::certs(&mut BufReader::new(Cursor::new(client.0.clone())))?;
// load appropriate Key as per the user request. The underlying signature algorithm
// of key generation determines the Signature Algorithm during the TLS Handskahe.
let read_keys = match &client.1 {
Key::RSA(k) => rsa_private_keys(&mut BufReader::new(Cursor::new(k.clone()))),
Key::ECC(k) => pkcs8_private_keys(&mut BufReader::new(Cursor::new(k.clone()))),
Key::RSA(k) => rustls_pemfile::rsa_private_keys(&mut BufReader::new(
Cursor::new(k.clone()),
)),
Key::ECC(k) => rustls_pemfile::pkcs8_private_keys(&mut BufReader::new(
Cursor::new(k.clone()),
)),
};
let keys = match read_keys {
Ok(v) => v,
Expand All @@ -77,12 +96,16 @@ pub async fn tls_connector(tls_config: &TlsConfiguration) -> Result<TlsConnector
None => return Err(Error::NoValidCertInChain),
};

config.set_single_client_cert(certs, key)?;
}
let certs = certs.into_iter().map(|cert| Certificate(cert)).collect();

config.with_single_cert(certs, PrivateKey(key))?
} else {
config.with_no_client_auth()
};

// Set ALPN
if let Some(alpn) = alpn.as_ref() {
config.set_protocols(&alpn);
config.alpn_protocols.extend_from_slice(alpn);
}

Arc::new(config)
Expand All @@ -100,7 +123,7 @@ pub async fn tls_connect(
let addr = options.broker_addr.as_str();
let port = options.port;
let connector = tls_connector(tls_config).await?;
let domain = DNSNameRef::try_from_ascii_str(&options.broker_addr)?;
let domain = ServerName::try_from(addr)?;
let tcp = TcpStream::connect((addr, port)).await?;
let tls = connector.connect(domain, tcp).await?;
Ok(tls)
Expand Down

0 comments on commit e0ee2ae

Please sign in to comment.