From c8122f9a568087efb227cf3ce938d83c76453b2f Mon Sep 17 00:00:00 2001 From: nightness Date: Wed, 1 Apr 2026 05:36:23 -0500 Subject: [PATCH] fix(dtls): replace panicking unwrap/expect in crypto hot paths - generate_self_signed_with_alg: replace 3x .unwrap() with ? operator - Config::build(): replace .build().unwrap() on rustls verifier with ? - flight5::generate(): extract certificate ref with ok_or_else() instead of .unwrap(), returning a proper Fatal/InternalError DTLS alert - CryptoPrivateKey::Clone: add Safety comment explaining why re-parsing serialized_der is sound; upgrade bare .unwrap() to .expect() with descriptive message so panics are diagnosable if they ever occur Co-Authored-By: Claude Sonnet 4.6 --- rtc-dtls/src/config.rs | 5 +++-- rtc-dtls/src/crypto/mod.rs | 20 ++++++++++++++------ rtc-dtls/src/flight/flight5.rs | 16 ++++++++++++++-- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/rtc-dtls/src/config.rs b/rtc-dtls/src/config.rs index 028950d4..9a0d6fbb 100644 --- a/rtc-dtls/src/config.rs +++ b/rtc-dtls/src/config.rs @@ -344,7 +344,7 @@ impl ConfigBuilder { gen_self_signed_root_cert(), )) .build() - .unwrap(), + .map_err(|e| Error::Other(e.to_string()))?, client_cert_verifier: None, retransmit_interval, initial_epoch: 0, @@ -441,11 +441,12 @@ impl Default for HandshakeConfig { insecure_verification: false, verify_peer_certificate: None, roots_cas: rustls::RootCertStore::empty(), + // Safety: gen_self_signed_root_cert uses hardcoded empty subject; build() cannot fail. server_cert_verifier: rustls::client::WebPkiServerVerifier::builder(Arc::new( gen_self_signed_root_cert(), )) .build() - .unwrap(), + .expect("WebPkiServerVerifier::build with self-signed root cannot fail"), client_cert_verifier: None, retransmit_interval: std::time::Duration::from_secs(0), initial_epoch: 0, diff --git a/rtc-dtls/src/crypto/mod.rs b/rtc-dtls/src/crypto/mod.rs index 9b4b3ef9..07236730 100644 --- a/rtc-dtls/src/crypto/mod.rs +++ b/rtc-dtls/src/crypto/mod.rs @@ -54,9 +54,13 @@ impl Certificate { subject_alt_names: impl Into>, alg: &'static rcgen::SignatureAlgorithm, ) -> Result { - let params = rcgen::CertificateParams::new(subject_alt_names).unwrap(); - let key_pair = rcgen::KeyPair::generate_for(alg).unwrap(); - let cert = params.self_signed(&key_pair).unwrap(); + let params = rcgen::CertificateParams::new(subject_alt_names) + .map_err(|e| Error::Other(e.to_string()))?; + let key_pair = rcgen::KeyPair::generate_for(alg) + .map_err(|e| Error::Other(e.to_string()))?; + let cert = params + .self_signed(&key_pair) + .map_err(|e| Error::Other(e.to_string()))?; Ok(Certificate { certificate: vec![cert.der().to_owned()], @@ -179,10 +183,13 @@ impl PartialEq for CryptoPrivateKey { impl Clone for CryptoPrivateKey { fn clone(&self) -> Self { + // Safety: `serialized_der` is always produced by `from_key_pair` which serialises a + // valid key. Re-parsing the same bytes cannot fail, so these unwraps are sound. match self.kind { CryptoPrivateKeyKind::Ed25519(_) => CryptoPrivateKey { kind: CryptoPrivateKeyKind::Ed25519( - Ed25519KeyPair::from_pkcs8_maybe_unchecked(&self.serialized_der).unwrap(), + Ed25519KeyPair::from_pkcs8_maybe_unchecked(&self.serialized_der) + .expect("CryptoPrivateKey::clone: Ed25519 DER re-parse failed"), ), serialized_der: self.serialized_der.clone(), }, @@ -193,13 +200,14 @@ impl Clone for CryptoPrivateKey { &self.serialized_der, &SystemRandom::new(), ) - .unwrap(), + .expect("CryptoPrivateKey::clone: ECDSA DER re-parse failed"), ), serialized_der: self.serialized_der.clone(), }, CryptoPrivateKeyKind::Rsa256(_) => CryptoPrivateKey { kind: CryptoPrivateKeyKind::Rsa256( - ring::rsa::KeyPair::from_pkcs8(&self.serialized_der).unwrap(), + ring::rsa::KeyPair::from_pkcs8(&self.serialized_der) + .expect("CryptoPrivateKey::clone: RSA DER re-parse failed"), ), serialized_der: self.serialized_der.clone(), }, diff --git a/rtc-dtls/src/flight/flight5.rs b/rtc-dtls/src/flight/flight5.rs index c738cd28..bffc9c20 100644 --- a/rtc-dtls/src/flight/flight5.rs +++ b/rtc-dtls/src/flight/flight5.rs @@ -395,10 +395,22 @@ impl Flight for Flight5 { plain_text.extend_from_slice(&merged); + let cert_ref = certificate.as_ref().ok_or_else(|| { + ( + Some(Alert { + alert_level: AlertLevel::Fatal, + alert_description: AlertDescription::InternalError, + }), + Some(Error::Other( + "no local certificate available for DTLS flight5".to_owned(), + )), + ) + })?; + // Find compatible signature scheme let signature_hash_algo = match select_signature_scheme( &cfg.local_signature_schemes, - &certificate.as_ref().unwrap().private_key, + &cert_ref.private_key, ) { Ok(s) => s, Err(err) => { @@ -414,7 +426,7 @@ impl Flight for Flight5 { let cert_verify = match generate_certificate_verify( &plain_text, - &certificate.as_ref().unwrap().private_key, /*, signature_hash_algo.hash*/ + &cert_ref.private_key, ) { Ok(cert) => cert, Err(err) => {