Skip to content

Commit 8d05bf7

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 8d05bf7

File tree

8 files changed

+59
-124
lines changed

8 files changed

+59
-124
lines changed

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: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
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::{pem::PemObject, CertificateDer, InvalidDnsNameError, PrivateKeyDer, ServerName},
75
ClientConfig, RootCertStore,
86
};
97
#[cfg(feature = "use-rustls-no-provider")]
@@ -12,8 +10,6 @@ use tokio_rustls::TlsConnector as RustlsConnector;
1210
#[cfg(feature = "use-rustls-no-provider")]
1311
use std::convert::TryFrom;
1412
#[cfg(feature = "use-rustls-no-provider")]
15-
use std::io::{BufReader, Cursor};
16-
#[cfg(feature = "use-rustls-no-provider")]
1713
use std::sync::Arc;
1814

1915
use crate::framed::AsyncReadWrite;
@@ -60,6 +56,10 @@ pub enum Error {
6056
/// No valid key found
6157
#[error("No valid key in chain")]
6258
NoValidKeyInChain,
59+
#[cfg(feature = "use-rustls-no-provider")]
60+
/// PEM parsing error
61+
#[error("PEM parsing error: {0}")]
62+
Pem(#[from] tokio_rustls::rustls::pki_types::pem::Error),
6363
#[cfg(feature = "use-native-tls")]
6464
#[error("Native TLS error {0}")]
6565
NativeTls(#[from] NativeTlsError),
@@ -75,8 +75,8 @@ pub async fn rustls_connector(tls_config: &TlsConfiguration) -> Result<RustlsCon
7575
} => {
7676
// Add ca to root store if the connection is TLS
7777
let mut root_cert_store = RootCertStore::empty();
78-
let certs = rustls_pemfile::certs(&mut BufReader::new(Cursor::new(ca)))
79-
.collect::<Result<Vec<_>, _>>()?;
78+
let certs: Vec<CertificateDer> =
79+
CertificateDer::pem_slice_iter(ca).collect::<Result<Vec<_>, _>>()?;
8080

8181
root_cert_store.add_parsable_certificates(certs);
8282

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

8989
// Add der encoded client cert and key
9090
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<_>, _>>()?;
91+
let certs: Vec<CertificateDer> =
92+
CertificateDer::pem_slice_iter(&client.0).collect::<Result<Vec<_>, _>>()?;
93+
9494
if certs.is_empty() {
9595
return Err(Error::NoValidClientCertInChain);
9696
}
9797

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-
98+
let key = PrivateKeyDer::from_pem_slice(&client.1)?;
11999
config.with_client_auth_cert(certs, key)?
120100
} else {
121101
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: 18 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,12 @@ 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::PemObject, CertificateDer, InvalidDnsNameError, PrivateKeyDer, ServerName,
18+
},
2519
ClientConfig, Error as TLSError, RootCertStore,
2620
},
2721
TlsConnector,
@@ -203,9 +197,11 @@ pub async fn tls_connect<P: AsRef<Path>>(
203197
) -> Result<Box<dyn N>, BridgeError> {
204198
let mut root_cert_store = RootCertStore::empty();
205199

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

210206
if root_cert_store.is_empty() {
211207
return Err(BridgeError::NoValidCertInChain);
@@ -218,18 +214,12 @@ pub async fn tls_connect<P: AsRef<Path>>(
218214
key: key_path,
219215
}) = client_auth_opt
220216
{
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-
};
217+
let certs_pem = fs::read(certs_path)?;
218+
let certs: Vec<CertificateDer> =
219+
CertificateDer::pem_slice_iter(&certs_pem).collect::<Result<Vec<_>, _>>()?;
220+
221+
let key_pem = fs::read(key_path)?;
222+
let key = PrivateKeyDer::from_pem_slice(&key_pem)?;
233223

234224
config.with_client_auth_cert(certs, key)?
235225
} else {
@@ -319,4 +309,7 @@ pub enum BridgeError {
319309
#[cfg(feature = "use-rustls")]
320310
#[error("Invalid trust_anchor")]
321311
NoValidCertInChain,
312+
#[cfg(feature = "use-rustls")]
313+
#[error("PEM parsing error: {0}")]
314+
Pem(#[from] tokio_rustls::rustls::pki_types::pem::Error),
322315
}

rumqttd/src/server/tls.rs

Lines changed: 24 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
use std::fs::File;
21
use tokio::net::TcpStream;
32

43
#[cfg(feature = "use-native-tls")]
54
use {
6-
std::io::Read, tokio_native_tls::native_tls,
5+
std::fs::File, std::io::Read, tokio_native_tls::native_tls,
76
tokio_native_tls::native_tls::Error as NativeTlsError,
87
};
98

@@ -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

@@ -57,6 +58,9 @@ pub enum Error {
5758
MissingTenantId,
5859
#[error("Tenant id missing in certificate")]
5960
CertificateParse,
61+
#[cfg(feature = "use-rustls")]
62+
#[error("PEM parsing error: {0}")]
63+
Pem(#[from] tokio_rustls::rustls::pki_types::pem::Error),
6064
}
6165

6266
#[cfg(feature = "verify-client-cert")]
@@ -198,14 +202,15 @@ impl TLSAcceptor {
198202

199203
let (certs, key) = {
200204
// 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))
204-
.collect::<Result<Vec<_>, _>>()
205-
.map_err(|_| Error::InvalidServerCert(cert_path.to_string()))?;
205+
let cert_pem = std::fs::read(cert_path)
206+
.map_err(|_| Error::ServerCertNotFound(cert_path.clone()))?;
207+
let certs: Vec<CertificateDer> =
208+
CertificateDer::pem_slice_iter(&cert_pem).collect::<Result<Vec<_>, _>>()?;
206209

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

210215
(certs, key)
211216
};
@@ -215,12 +220,15 @@ impl TLSAcceptor {
215220
// client authentication with a CA. CA isn't required otherwise
216221
#[cfg(feature = "verify-client-cert")]
217222
let builder = {
218-
let ca_file = File::open(ca_path);
219-
let ca_file = ca_file.map_err(|_| Error::CaFileNotFound(ca_path.clone()))?;
220-
let ca_file = &mut BufReader::new(ca_file);
221-
let ca_cert = rustls_pemfile::certs(ca_file)
223+
let ca_pem =
224+
std::fs::read(ca_path).map_err(|_| Error::CaFileNotFound(ca_path.clone()))?;
225+
let ca_certs: Vec<CertificateDer> =
226+
CertificateDer::pem_slice_iter(&ca_pem).collect::<Result<Vec<_>, _>>()?;
227+
228+
let ca_cert = ca_certs
229+
.into_iter()
222230
.next()
223-
.ok_or_else(|| Error::InvalidCACert(ca_path.to_string()))??;
231+
.ok_or_else(|| Error::InvalidCACert(ca_path.to_string()))?;
224232

225233
let mut store = RootCertStore::empty();
226234
store
@@ -245,38 +253,3 @@ impl TLSAcceptor {
245253
Ok(TLSAcceptor::Rustls { acceptor })
246254
}
247255
}
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)