Skip to content

[peer_connection] allow persistent certificates #204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 59 additions & 18 deletions src/peer_connection/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Vec<RTCDtlsFingerprint>> {
Expand Down Expand Up @@ -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<String> {
Ok(self.x509_cert.serialize_pem()?)
}
}

#[cfg(test)]
Expand All @@ -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(())
}
Expand All @@ -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)

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -355,18 +378,36 @@ 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);
//TODO: assert_eq!(pem, pem2);

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(())
}
}
2 changes: 1 addition & 1 deletion src/peer_connection/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down