Skip to content

Commit 112e92e

Browse files
committed
fix: migrate from rustls-pemfile to rustls-pki-types
Replace deprecated rustls-pemfile with rustls-pki-types PemObject API. The rustls-pemfile crate is unmaintained. - Use CertificateDer::pem_slice_iter() for certificate parsing - Use PrivateKeyDer::from_pem_slice() for key parsing - Remove rustls-pemfile dependency - Remove obsolete first_private_key_in_pemfile() helper function Closes #1010 Fixes RUSTSEC-2025-0134
1 parent 2167da0 commit 112e92e

8 files changed

Lines changed: 52 additions & 116 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rumqttc/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111
### Changed
12+
- Migrated from deprecated `rustls-pemfile` to `rustls-pki-types` PEM parsing API
1213
### Deprecated
1314
### Removed
1415
### Fixed

rumqttc/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ rustdoc-args = ["--cfg", "docsrs"]
1818
[features]
1919
default = ["use-rustls"]
2020
use-rustls = ["use-rustls-no-provider", "tokio-rustls/default"]
21-
use-rustls-no-provider = ["dep:tokio-rustls", "dep:rustls-webpki", "dep:rustls-pemfile", "dep:rustls-native-certs"]
21+
use-rustls-no-provider = ["dep:tokio-rustls", "dep:rustls-webpki", "dep:rustls-native-certs"]
2222
use-native-tls = ["dep:tokio-native-tls", "dep:native-tls"]
2323
websocket = ["dep:async-tungstenite", "dep:ws_stream_tungstenite", "dep:http"]
2424
proxy = ["dep:async-http-proxy"]
@@ -36,7 +36,6 @@ thiserror = "2.0.8"
3636
# rustls
3737
tokio-rustls = { version = "0.26.0", optional = true, default-features = false }
3838
rustls-webpki = { version = "0.102.8", optional = true }
39-
rustls-pemfile = { version = "2.2.0", optional = true }
4039
rustls-native-certs = { version = "0.8.1", optional = true }
4140
# websockets
4241
async-tungstenite = { version = "0.29.0", default-features = false, features = ["tokio-rustls-native-certs"], optional = true }

rumqttc/src/tls.rs

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#[cfg(feature = "use-rustls-no-provider")]
2-
use rustls_pemfile::Item;
3-
#[cfg(feature = "use-rustls-no-provider")]
42
use tokio_rustls::rustls::{
53
self,
6-
pki_types::{InvalidDnsNameError, ServerName},
4+
pki_types::{
5+
pem::Error as PemError, pem::PemObject, CertificateDer, InvalidDnsNameError, PrivateKeyDer,
6+
ServerName,
7+
},
78
ClientConfig, RootCertStore,
89
};
910
#[cfg(feature = "use-rustls-no-provider")]
@@ -12,8 +13,6 @@ use tokio_rustls::TlsConnector as RustlsConnector;
1213
#[cfg(feature = "use-rustls-no-provider")]
1314
use std::convert::TryFrom;
1415
#[cfg(feature = "use-rustls-no-provider")]
15-
use std::io::{BufReader, Cursor};
16-
#[cfg(feature = "use-rustls-no-provider")]
1716
use std::sync::Arc;
1817

1918
use crate::framed::AsyncReadWrite;
@@ -60,6 +59,10 @@ pub enum Error {
6059
/// No valid key found
6160
#[error("No valid key in chain")]
6261
NoValidKeyInChain,
62+
#[cfg(feature = "use-rustls-no-provider")]
63+
/// PEM parsing error
64+
#[error("PEM parsing error: {0}")]
65+
Pem(#[from] PemError),
6366
#[cfg(feature = "use-native-tls")]
6467
#[error("Native TLS error {0}")]
6568
NativeTls(#[from] NativeTlsError),
@@ -75,8 +78,8 @@ pub async fn rustls_connector(tls_config: &TlsConfiguration) -> Result<RustlsCon
7578
} => {
7679
// Add ca to root store if the connection is TLS
7780
let mut root_cert_store = RootCertStore::empty();
78-
let certs = rustls_pemfile::certs(&mut BufReader::new(Cursor::new(ca)))
79-
.collect::<Result<Vec<_>, _>>()?;
81+
let certs: Vec<CertificateDer> =
82+
CertificateDer::pem_slice_iter(ca).collect::<Result<Vec<_>, _>>()?;
8083

8184
root_cert_store.add_parsable_certificates(certs);
8285

@@ -88,34 +91,14 @@ pub async fn rustls_connector(tls_config: &TlsConfiguration) -> Result<RustlsCon
8891

8992
// Add der encoded client cert and key
9093
let mut config = if let Some(client) = client_auth.as_ref() {
91-
let certs =
92-
rustls_pemfile::certs(&mut BufReader::new(Cursor::new(client.0.clone())))
93-
.collect::<Result<Vec<_>, _>>()?;
94+
let certs: Vec<CertificateDer> =
95+
CertificateDer::pem_slice_iter(&client.0).collect::<Result<Vec<_>, _>>()?;
96+
9497
if certs.is_empty() {
9598
return Err(Error::NoValidClientCertInChain);
9699
}
97100

98-
// Create buffer for key file
99-
let mut key_buffer = BufReader::new(Cursor::new(client.1.clone()));
100-
101-
// Read PEM items until we find a valid key.
102-
let key = loop {
103-
let item = rustls_pemfile::read_one(&mut key_buffer)?;
104-
match item {
105-
Some(Item::Sec1Key(key)) => {
106-
break key.into();
107-
}
108-
Some(Item::Pkcs1Key(key)) => {
109-
break key.into();
110-
}
111-
Some(Item::Pkcs8Key(key)) => {
112-
break key.into();
113-
}
114-
None => return Err(Error::NoValidKeyInChain),
115-
_ => {}
116-
}
117-
};
118-
101+
let key = PrivateKeyDer::from_pem_slice(&client.1)?;
119102
config.with_client_auth_cert(certs, key)?
120103
} else {
121104
config.with_no_client_auth()

rumqttd/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111
### Changed
12+
- Migrated from deprecated `rustls-pemfile` to `rustls-pki-types` PEM parsing API
1213
### Deprecated
1314
### Removed
1415
### Fixed

rumqttd/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ tokio-util = { version = "0.7", features = ["codec"], optional = true }
2323
tokio-rustls = { version = "0.25.0", optional = true }
2424
rustls-webpki = { version = "0.102.2", optional = true }
2525
tokio-native-tls = { version = "0.3.1", optional = true }
26-
rustls-pemfile = { version = "2.1.0", optional = true }
2726
async-tungstenite = { version = "0.25", default-features = false, features = ["tokio-runtime"], optional = true }
2827
ws_stream_tungstenite = { version= "0.13", default-features = false, features = ["tokio_io"], optional = true }
2928
x509-parser = {version= "0.15.1", optional = true}
@@ -42,7 +41,7 @@ subtle = "2.5"
4241

4342
[features]
4443
default = ["use-rustls", "websocket"]
45-
use-rustls = ["dep:tokio-rustls", "dep:rustls-webpki", "dep:rustls-pemfile", "dep:x509-parser"]
44+
use-rustls = ["dep:tokio-rustls", "dep:rustls-webpki", "dep:x509-parser"]
4645
use-native-tls = ["dep:tokio-native-tls", "dep:x509-parser"]
4746
websocket = ["dep:async-tungstenite", "dep:tokio-util", "dep:futures-util", "dep:ws_stream_tungstenite"]
4847
verify-client-cert = []

rumqttd/src/link/bridge.rs

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
use flume::Sender;
22

33
#[cfg(feature = "use-rustls")]
4-
use std::{
5-
fs,
6-
io::{BufReader, Cursor},
7-
path::Path,
8-
sync::Arc,
9-
};
4+
use std::{fs, path::Path, sync::Arc};
105

116
use std::{io, net::AddrParseError, time::Duration};
127

@@ -15,13 +10,13 @@ use tokio::{
1510
time::{sleep, sleep_until, Instant},
1611
};
1712

18-
#[cfg(feature = "use-rustls")]
19-
use rustls_pemfile::Item;
20-
2113
#[cfg(feature = "use-rustls")]
2214
use tokio_rustls::{
2315
rustls::{
24-
pki_types::{InvalidDnsNameError, ServerName},
16+
pki_types::{
17+
pem::Error as PemError, pem::PemObject, CertificateDer, InvalidDnsNameError,
18+
PrivateKeyDer, ServerName,
19+
},
2520
ClientConfig, Error as TLSError, RootCertStore,
2621
},
2722
TlsConnector,
@@ -203,9 +198,11 @@ pub async fn tls_connect<P: AsRef<Path>>(
203198
) -> Result<Box<dyn N>, BridgeError> {
204199
let mut root_cert_store = RootCertStore::empty();
205200

206-
for cert in rustls_pemfile::certs(&mut BufReader::new(Cursor::new(fs::read(ca_file)?))) {
207-
root_cert_store.add(cert?)?;
208-
}
201+
let ca_pem = fs::read(ca_file)?;
202+
let certs: Vec<CertificateDer> =
203+
CertificateDer::pem_slice_iter(&ca_pem).collect::<Result<Vec<_>, _>>()?;
204+
205+
root_cert_store.add_parsable_certificates(certs);
209206

210207
if root_cert_store.is_empty() {
211208
return Err(BridgeError::NoValidCertInChain);
@@ -218,18 +215,12 @@ pub async fn tls_connect<P: AsRef<Path>>(
218215
key: key_path,
219216
}) = client_auth_opt
220217
{
221-
let certs = rustls_pemfile::certs(&mut BufReader::new(Cursor::new(fs::read(certs_path)?)))
222-
.collect::<Result<Vec<_>, _>>()?;
223-
224-
let key = loop {
225-
match rustls_pemfile::read_one(&mut BufReader::new(Cursor::new(fs::read(key_path)?)))? {
226-
Some(Item::Pkcs1Key(key)) => break key.into(),
227-
Some(Item::Pkcs8Key(key)) => break key.into(),
228-
Some(Item::Sec1Key(key)) => break key.into(),
229-
None => return Err(BridgeError::NoValidCertInChain),
230-
_ => {}
231-
};
232-
};
218+
let certs_pem = fs::read(certs_path)?;
219+
let certs: Vec<CertificateDer> =
220+
CertificateDer::pem_slice_iter(&certs_pem).collect::<Result<Vec<_>, _>>()?;
221+
222+
let key_pem = fs::read(key_path)?;
223+
let key = PrivateKeyDer::from_pem_slice(&key_pem)?;
233224

234225
config.with_client_auth_cert(certs, key)?
235226
} else {
@@ -319,4 +310,7 @@ pub enum BridgeError {
319310
#[cfg(feature = "use-rustls")]
320311
#[error("Invalid trust_anchor")]
321312
NoValidCertInChain,
313+
#[cfg(feature = "use-rustls")]
314+
#[error("PEM parsing error: {0}")]
315+
Pem(#[from] PemError),
322316
}

rumqttd/src/server/tls.rs

Lines changed: 13 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::fs::File;
21
use tokio::net::TcpStream;
32

43
#[cfg(feature = "use-native-tls")]
@@ -12,9 +11,11 @@ use crate::TlsConfig;
1211
use tokio_rustls::rustls::{server::WebPkiClientVerifier, RootCertStore};
1312
#[cfg(feature = "use-rustls")]
1413
use {
15-
rustls_pemfile::Item,
16-
std::{io::BufReader, sync::Arc},
17-
tokio_rustls::rustls::{pki_types::PrivateKeyDer, Error as RustlsError, ServerConfig},
14+
std::sync::Arc,
15+
tokio_rustls::rustls::{
16+
pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer},
17+
Error as RustlsError, ServerConfig,
18+
},
1819
tracing::error,
1920
};
2021

@@ -198,14 +199,18 @@ impl TLSAcceptor {
198199

199200
let (certs, key) = {
200201
// Get certificates
201-
let cert_file = File::open(cert_path);
202-
let cert_file = cert_file.map_err(|_| Error::ServerCertNotFound(cert_path.clone()))?;
203-
let certs = rustls_pemfile::certs(&mut BufReader::new(cert_file))
202+
203+
let cert_pem = std::fs::read(cert_path)
204+
.map_err(|_| Error::ServerCertNotFound(cert_path.clone()))?;
205+
let certs: Vec<CertificateDer> = CertificateDer::pem_slice_iter(&cert_pem)
204206
.collect::<Result<Vec<_>, _>>()
205207
.map_err(|_| Error::InvalidServerCert(cert_path.to_string()))?;
206208

207209
// Get private key
208-
let key = first_private_key_in_pemfile(key_path)?;
210+
let key_pem =
211+
std::fs::read(key_path).map_err(|_| Error::ServerKeyNotFound(key_path.clone()))?;
212+
let key = PrivateKeyDer::from_pem_slice(&key_pem)
213+
.map_err(|_| Error::InvalidServerKey(key_path.to_string()))?;
209214

210215
(certs, key)
211216
};
@@ -245,38 +250,3 @@ impl TLSAcceptor {
245250
Ok(TLSAcceptor::Rustls { acceptor })
246251
}
247252
}
248-
249-
#[cfg(feature = "use-rustls")]
250-
/// Get the first private key in a PEM file
251-
fn first_private_key_in_pemfile(key_path: &String) -> Result<PrivateKeyDer<'static>, Error> {
252-
// Get private key
253-
let key_file = File::open(key_path);
254-
let key_file = key_file.map_err(|_| Error::ServerKeyNotFound(key_path.clone()))?;
255-
256-
let rd = &mut BufReader::new(key_file);
257-
258-
// keep reading Items one by one to find a Key, return error if none found.
259-
loop {
260-
let item = rustls_pemfile::read_one(rd).map_err(|err| {
261-
error!("Error reading key file: {:?}", err);
262-
Error::InvalidServerKey(key_path.clone())
263-
})?;
264-
265-
match item {
266-
Some(Item::Sec1Key(key)) => {
267-
return Ok(key.into());
268-
}
269-
Some(Item::Pkcs1Key(key)) => {
270-
return Ok(key.into());
271-
}
272-
Some(Item::Pkcs8Key(key)) => {
273-
return Ok(key.into());
274-
}
275-
None => {
276-
error!("No private key found in {:?}", key_path);
277-
return Err(Error::InvalidServerKey(key_path.clone()));
278-
}
279-
_ => {}
280-
}
281-
}
282-
}

0 commit comments

Comments
 (0)