diff --git a/src/peer_connection/certificate.rs b/src/peer_connection/certificate.rs index d4ad36ef9..2a1687122 100644 --- a/src/peer_connection/certificate.rs +++ b/src/peer_connection/certificate.rs @@ -15,11 +15,13 @@ use tokio::sync::Mutex; use waitgroup::Worker; /// Certificate represents a x509Cert used to authenticate WebRTC communications. +#[derive(Clone)] pub struct RTCCertificate { pub(crate) certificate: dtls::crypto::Certificate, pub(crate) stats_id: String, - pub(crate) x509_cert: rcgen::Certificate, - pub(crate) expires: SystemTime, + + pem: String, + expires: SystemTime, } /// Equals determines if two certificates are identical by comparing only certificate @@ -100,16 +102,45 @@ impl RTCCertificate { .unwrap() .as_nanos() as u64 ), - x509_cert, + pem: x509_cert.serialize_pem()?, expires, }) } + /// Constructs a `RTCCertificate` from an existing certificate. + /// + /// Use this method when you have a persistent certificate (i.e. you don't want to generate a + /// new one for each DTLS connection). + pub fn from_existing( + certificate: dtls::crypto::Certificate, + pem: &str, + expires: SystemTime, + ) -> Self { + Self { + certificate, + stats_id: format!( + "certificate-{}", + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as u64 + ), + pem: pem.to_owned(), + expires, + } + } + /// expires returns the timestamp after which this certificate is no longer valid. pub fn expires(&self) -> SystemTime { self.expires } + /// pem returns the certificate encoded as two PEM blocks: one for the X509 certificate and the + /// other for the private key. + pub fn pem(&self) -> &str { + &self.pem + } + /// get_fingerprints returns certificate fingerprints, one of which /// is computed with the digest algorithm used in the certificate signature. pub fn get_fingerprints(&self) -> Result> { @@ -239,12 +270,6 @@ impl RTCCertificate { RTCCertificate::from_params(params) } - - /// PEM returns the certificate encoded as two pem block: once for the X509 - /// certificate and the other for the private key - pub fn pem(&self) -> Result { - Ok(self.x509_cert.serialize_pem()?) - } } #[cfg(test)] @@ -259,9 +284,8 @@ mod test { let kp_pem = kp.serialize_pem(); let cert = Certificate::generate_certificate(kp)?; - let cert_pem = cert.x509_cert.serialize_pem()?; - //_, err = tls.X509KeyPair(certPEM, skPEM) + //_, err = tls.X509KeyPair(cert.pem(), skPEM) */ Ok(()) } @@ -273,8 +297,7 @@ mod test { assert!(kp_pem.contains("PRIVATE KEY")); let cert = RTCCertificate::from_key_pair(kp)?; - let cert_pem = cert.x509_cert.serialize_pem()?; - assert!(cert_pem.contains("CERTIFICATE")); + assert!(cert.pem().contains("CERTIFICATE")); //_, err = tls.X509KeyPair(certPEM, skPEM) @@ -304,14 +327,14 @@ mod test { let kp1 = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256)?; let kp1_pem = kp1.serialize_pem(); let cert1 = RTCCertificate::from_key_pair(kp1)?; - let cert1_pem = cert1.pem()?; + let cert1_pem = cert1.pem(); let kp2 = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256)?; let _cert2 = RTCCertificate::from_key_pair(kp2)?; let kp3 = KeyPair::from_pem(kp1_pem.as_str())?; let kp3_pem = kp3.serialize_pem(); - let _cert3 = RTCCertificate::from_pem(cert1_pem.as_str(), kp3)?; + let _cert3 = RTCCertificate::from_pem(cert1_pem, kp3)?; assert_eq!(kp1_pem, kp3_pem); //assert!(cert1 != cert2); @@ -355,13 +378,13 @@ mod test { let kp = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256)?; let kp_pem = kp.serialize_pem(); let cert = RTCCertificate::from_key_pair(kp)?; - let pem = cert.pem()?; + let pem = cert.pem(); log::info!("{}", pem); let kp2 = KeyPair::from_pem(kp_pem.as_str())?; let kp2_pem = kp2.serialize_pem(); - let cert2 = RTCCertificate::from_pem(pem.as_str(), kp2)?; - let pem2 = cert2.pem()?; + let cert2 = RTCCertificate::from_pem(pem, kp2)?; + let pem2 = cert2.pem(); log::info!("{}", pem2); assert_eq!(kp_pem, kp2_pem); @@ -369,4 +392,22 @@ mod test { Ok(()) } + + #[test] + fn test_from_existing() -> Result<()> { + // NOTE `dtls_cert` key pair and `key_pair` are different, but it's fine here. + let key_pair = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256)?; + let dtls_cert = dtls::crypto::Certificate::generate_self_signed(["localhost".to_owned()])?; + + let expires = SystemTime::now(); + let pem = key_pair.serialize_pem(); + + let cert = RTCCertificate::from_existing(dtls_cert, &pem, expires); + + assert_ne!("", cert.stats_id); + assert_eq!(expires, cert.expires()); + assert_eq!(pem, cert.pem()); + + Ok(()) + } } diff --git a/src/peer_connection/configuration.rs b/src/peer_connection/configuration.rs index 7619e3d9d..6820dcc40 100644 --- a/src/peer_connection/configuration.rs +++ b/src/peer_connection/configuration.rs @@ -10,7 +10,7 @@ use crate::peer_connection::policy::sdp_semantics::RTCSdpSemantics; /// Configurations may be set up once and reused across multiple connections. /// Configurations are treated as readonly. As long as they are unmodified, /// they are safe for concurrent use. -#[derive(Default)] +#[derive(Default, Clone)] pub struct RTCConfiguration { /// iceservers defines a slice describing servers available to be used by /// ICE, such as STUN and TURN servers.