Skip to content

Commit d8b0ba0

Browse files
authored
feat(rumqttc): remove Key enum and accept all valid key formats (#752)
1 parent c8e5c87 commit d8b0ba0

File tree

4 files changed

+37
-42
lines changed

4 files changed

+37
-42
lines changed

rumqttc/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111
- Expose `EventLoop::clean` to allow triggering shutdown and subsequent storage of pending requests
12+
- Support for all variants of TLS key formats currently supported by Rustls: `PKCS#1`, `PKCS#8`, `RFC5915`. In practice we should now support all RSA keys and ECC keys in `DER` and `SEC1` encoding. Previously only `PKCS#1` and `PKCS#8` where supported.
13+
- TLS Error variants: `NoValidClientCertInChain`, `NoValidKeyInChain`.
1214

1315
### Changed
1416
- Synchronous client methods take `&self` instead of `&mut self` (#646)
17+
- Removed the `Key` enum: users do not need to specify the TLS key variant in the `TlsConfiguration` anymore, this is inferred automatically.
18+
To update your code simply remove `Key::ECC()` or `Key::RSA()` from the initialization.
1519

1620
### Deprecated
1721

rumqttc/examples/tls2.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::error::Error;
44
#[cfg(feature = "use-rustls")]
55
#[tokio::main]
66
async fn main() -> Result<(), Box<dyn Error>> {
7-
use rumqttc::{self, AsyncClient, Key, MqttOptions, TlsConfiguration, Transport};
7+
use rumqttc::{self, AsyncClient, MqttOptions, TlsConfiguration, Transport};
88

99
pretty_env_logger::init();
1010
color_backtrace::install();
@@ -23,7 +23,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
2323
let transport = Transport::Tls(TlsConfiguration::Simple {
2424
ca,
2525
alpn: None,
26-
client_auth: Some((client_cert, Key::RSA(client_key))),
26+
client_auth: Some((client_cert, client_key)),
2727
});
2828

2929
mqttoptions.set_transport(transport);

rumqttc/src/lib.rs

+3-10
Original file line numberDiff line numberDiff line change
@@ -204,13 +204,6 @@ impl Request {
204204
}
205205
}
206206

207-
/// Key type for TLS authentication
208-
#[derive(Clone, Debug, PartialEq, Eq)]
209-
pub enum Key {
210-
RSA(Vec<u8>),
211-
ECC(Vec<u8>),
212-
}
213-
214207
impl From<Publish> for Request {
215208
fn from(publish: Publish) -> Request {
216209
Request::Publish(publish)
@@ -266,7 +259,7 @@ impl Transport {
266259
#[cfg(feature = "use-rustls")]
267260
pub fn tls(
268261
ca: Vec<u8>,
269-
client_auth: Option<(Vec<u8>, Key)>,
262+
client_auth: Option<(Vec<u8>, Vec<u8>)>,
270263
alpn: Option<Vec<Vec<u8>>>,
271264
) -> Self {
272265
let config = TlsConfiguration::Simple {
@@ -300,7 +293,7 @@ impl Transport {
300293
#[cfg_attr(docsrs, doc(cfg(all(feature = "use-rustls", feature = "websocket"))))]
301294
pub fn wss(
302295
ca: Vec<u8>,
303-
client_auth: Option<(Vec<u8>, Key)>,
296+
client_auth: Option<(Vec<u8>, Vec<u8>)>,
304297
alpn: Option<Vec<Vec<u8>>>,
305298
) -> Self {
306299
let config = TlsConfiguration::Simple {
@@ -336,7 +329,7 @@ pub enum TlsConfiguration {
336329
/// alpn settings
337330
alpn: Option<Vec<Vec<u8>>>,
338331
/// tls client_authentication
339-
client_auth: Option<(Vec<u8>, Key)>,
332+
client_auth: Option<(Vec<u8>, Vec<u8>)>,
340333
},
341334
#[cfg(feature = "use-native-tls")]
342335
SimpleNative {

rumqttc/src/tls.rs

+28-30
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#[cfg(feature = "use-rustls")]
2+
use rustls_pemfile::Item;
3+
#[cfg(feature = "use-rustls")]
24
use tokio_rustls::rustls;
35
#[cfg(feature = "use-rustls")]
46
use tokio_rustls::rustls::client::InvalidDnsNameError;
@@ -9,8 +11,6 @@ use tokio_rustls::rustls::{
911
#[cfg(feature = "use-rustls")]
1012
use tokio_rustls::TlsConnector as RustlsConnector;
1113

12-
#[cfg(feature = "use-rustls")]
13-
use crate::Key;
1414
#[cfg(feature = "use-rustls")]
1515
use std::convert::TryFrom;
1616
#[cfg(feature = "use-rustls")]
@@ -51,21 +51,22 @@ pub enum Error {
5151
#[error("TLS error: {0}")]
5252
TLS(#[from] rustls::Error),
5353
#[cfg(feature = "use-rustls")]
54-
/// No valid certificate in chain
55-
#[error("No valid certificate in chain")]
54+
/// No valid CA cert found
55+
#[error("No valid CA certificate provided")]
5656
NoValidCertInChain,
57+
#[cfg(feature = "use-rustls")]
58+
/// No valid client cert found
59+
#[error("No valid certificate for client authentication in chain")]
60+
NoValidClientCertInChain,
61+
#[cfg(feature = "use-rustls")]
62+
/// No valid key found
63+
#[error("No valid key in chain")]
64+
NoValidKeyInChain,
5765
#[cfg(feature = "use-native-tls")]
5866
#[error("Native TLS error {0}")]
5967
NativeTls(#[from] NativeTlsError),
6068
}
6169

62-
// // The cert handling functions return unit right now, this is a shortcut
63-
// impl From<()> for Error {
64-
// fn from(_: ()) -> Self {
65-
// Error::NoValidCertInChain
66-
// }
67-
// }
68-
6970
#[cfg(feature = "use-rustls")]
7071
pub async fn rustls_connector(tls_config: &TlsConfiguration) -> Result<RustlsConnector, Error> {
7172
let config = match tls_config {
@@ -104,29 +105,26 @@ pub async fn rustls_connector(tls_config: &TlsConfiguration) -> Result<RustlsCon
104105
let mut config = if let Some(client) = client_auth.as_ref() {
105106
let certs =
106107
rustls_pemfile::certs(&mut BufReader::new(Cursor::new(client.0.clone())))?;
107-
// load appropriate Key as per the user request. The underlying signature algorithm
108-
// of key generation determines the Signature Algorithm during the TLS Handskahe.
109-
let read_keys = match &client.1 {
110-
Key::RSA(k) => rustls_pemfile::rsa_private_keys(&mut BufReader::new(
111-
Cursor::new(k.clone()),
112-
)),
113-
Key::ECC(k) => rustls_pemfile::pkcs8_private_keys(&mut BufReader::new(
114-
Cursor::new(k.clone()),
115-
)),
116-
};
117-
let keys = match read_keys {
118-
Ok(v) => v,
119-
Err(_e) => return Err(Error::NoValidCertInChain),
120-
};
108+
if certs.is_empty() {
109+
return Err(Error::NoValidClientCertInChain);
110+
}
121111

122-
// Get the first key. Error if it's not valid
123-
let key = match keys.first() {
124-
Some(k) => k.clone(),
125-
None => return Err(Error::NoValidCertInChain),
112+
// Create buffer for key file
113+
let mut key_buffer = BufReader::new(Cursor::new(client.1.clone()));
114+
115+
// Read PEM items until we find a valid key.
116+
let key = loop {
117+
let item = rustls_pemfile::read_one(&mut key_buffer)?;
118+
match item {
119+
Some(Item::ECKey(key) | Item::RSAKey(key) | Item::PKCS8Key(key)) => {
120+
break key;
121+
}
122+
None => return Err(Error::NoValidKeyInChain),
123+
_ => {}
124+
}
126125
};
127126

128127
let certs = certs.into_iter().map(Certificate).collect();
129-
130128
config.with_client_auth_cert(certs, PrivateKey(key))?
131129
} else {
132130
config.with_no_client_auth()

0 commit comments

Comments
 (0)