Skip to content

Commit 46fd086

Browse files
authored
[peer_connection] allow persistent certificates (#204)
Closes #168 This PR adds `RTCCertificate::from_existing` method, which constructs `RTCCertificate` from an existing DTLS certificate. An existing certificate might be needed in cases like [this](libp2p/rust-libp2p#2622) where you need DTLS identity to be fixed for some period of time (whole duration of the certificate or some part of it). * peer_connection: replace x509_cert with pem field also, make `pem` and `expires` fields private and add `RTCCertificate::from_existing` method, which gives a way to construct a `RTCCertificate` using an already generated certificate (as opposed to generating a new one using `from_params` or `from_key_pair` methods). * make RTCConfiguration clonable otherwise, it's not possible to reuse the same config across N peer connections.
1 parent 9db7d2e commit 46fd086

File tree

2 files changed

+60
-19
lines changed

2 files changed

+60
-19
lines changed

src/peer_connection/certificate.rs

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ use tokio::sync::Mutex;
1515
use waitgroup::Worker;
1616

1717
/// Certificate represents a x509Cert used to authenticate WebRTC communications.
18+
#[derive(Clone)]
1819
pub struct RTCCertificate {
1920
pub(crate) certificate: dtls::crypto::Certificate,
2021
pub(crate) stats_id: String,
21-
pub(crate) x509_cert: rcgen::Certificate,
22-
pub(crate) expires: SystemTime,
22+
23+
pem: String,
24+
expires: SystemTime,
2325
}
2426

2527
/// Equals determines if two certificates are identical by comparing only certificate
@@ -100,16 +102,45 @@ impl RTCCertificate {
100102
.unwrap()
101103
.as_nanos() as u64
102104
),
103-
x509_cert,
105+
pem: x509_cert.serialize_pem()?,
104106
expires,
105107
})
106108
}
107109

110+
/// Constructs a `RTCCertificate` from an existing certificate.
111+
///
112+
/// Use this method when you have a persistent certificate (i.e. you don't want to generate a
113+
/// new one for each DTLS connection).
114+
pub fn from_existing(
115+
certificate: dtls::crypto::Certificate,
116+
pem: &str,
117+
expires: SystemTime,
118+
) -> Self {
119+
Self {
120+
certificate,
121+
stats_id: format!(
122+
"certificate-{}",
123+
SystemTime::now()
124+
.duration_since(UNIX_EPOCH)
125+
.unwrap()
126+
.as_nanos() as u64
127+
),
128+
pem: pem.to_owned(),
129+
expires,
130+
}
131+
}
132+
108133
/// expires returns the timestamp after which this certificate is no longer valid.
109134
pub fn expires(&self) -> SystemTime {
110135
self.expires
111136
}
112137

138+
/// pem returns the certificate encoded as two PEM blocks: one for the X509 certificate and the
139+
/// other for the private key.
140+
pub fn pem(&self) -> &str {
141+
&self.pem
142+
}
143+
113144
/// get_fingerprints returns certificate fingerprints, one of which
114145
/// is computed with the digest algorithm used in the certificate signature.
115146
pub fn get_fingerprints(&self) -> Result<Vec<RTCDtlsFingerprint>> {
@@ -239,12 +270,6 @@ impl RTCCertificate {
239270

240271
RTCCertificate::from_params(params)
241272
}
242-
243-
/// PEM returns the certificate encoded as two pem block: once for the X509
244-
/// certificate and the other for the private key
245-
pub fn pem(&self) -> Result<String> {
246-
Ok(self.x509_cert.serialize_pem()?)
247-
}
248273
}
249274

250275
#[cfg(test)]
@@ -259,9 +284,8 @@ mod test {
259284
let kp_pem = kp.serialize_pem();
260285
261286
let cert = Certificate::generate_certificate(kp)?;
262-
let cert_pem = cert.x509_cert.serialize_pem()?;
263287
264-
//_, err = tls.X509KeyPair(certPEM, skPEM)
288+
//_, err = tls.X509KeyPair(cert.pem(), skPEM)
265289
*/
266290
Ok(())
267291
}
@@ -273,8 +297,7 @@ mod test {
273297
assert!(kp_pem.contains("PRIVATE KEY"));
274298

275299
let cert = RTCCertificate::from_key_pair(kp)?;
276-
let cert_pem = cert.x509_cert.serialize_pem()?;
277-
assert!(cert_pem.contains("CERTIFICATE"));
300+
assert!(cert.pem().contains("CERTIFICATE"));
278301

279302
//_, err = tls.X509KeyPair(certPEM, skPEM)
280303

@@ -304,14 +327,14 @@ mod test {
304327
let kp1 = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256)?;
305328
let kp1_pem = kp1.serialize_pem();
306329
let cert1 = RTCCertificate::from_key_pair(kp1)?;
307-
let cert1_pem = cert1.pem()?;
330+
let cert1_pem = cert1.pem();
308331

309332
let kp2 = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256)?;
310333
let _cert2 = RTCCertificate::from_key_pair(kp2)?;
311334

312335
let kp3 = KeyPair::from_pem(kp1_pem.as_str())?;
313336
let kp3_pem = kp3.serialize_pem();
314-
let _cert3 = RTCCertificate::from_pem(cert1_pem.as_str(), kp3)?;
337+
let _cert3 = RTCCertificate::from_pem(cert1_pem, kp3)?;
315338

316339
assert_eq!(kp1_pem, kp3_pem);
317340
//assert!(cert1 != cert2);
@@ -355,18 +378,36 @@ mod test {
355378
let kp = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256)?;
356379
let kp_pem = kp.serialize_pem();
357380
let cert = RTCCertificate::from_key_pair(kp)?;
358-
let pem = cert.pem()?;
381+
let pem = cert.pem();
359382
log::info!("{}", pem);
360383

361384
let kp2 = KeyPair::from_pem(kp_pem.as_str())?;
362385
let kp2_pem = kp2.serialize_pem();
363-
let cert2 = RTCCertificate::from_pem(pem.as_str(), kp2)?;
364-
let pem2 = cert2.pem()?;
386+
let cert2 = RTCCertificate::from_pem(pem, kp2)?;
387+
let pem2 = cert2.pem();
365388
log::info!("{}", pem2);
366389

367390
assert_eq!(kp_pem, kp2_pem);
368391
//TODO: assert_eq!(pem, pem2);
369392

370393
Ok(())
371394
}
395+
396+
#[test]
397+
fn test_from_existing() -> Result<()> {
398+
// NOTE `dtls_cert` key pair and `key_pair` are different, but it's fine here.
399+
let key_pair = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256)?;
400+
let dtls_cert = dtls::crypto::Certificate::generate_self_signed(["localhost".to_owned()])?;
401+
402+
let expires = SystemTime::now();
403+
let pem = key_pair.serialize_pem();
404+
405+
let cert = RTCCertificate::from_existing(dtls_cert, &pem, expires);
406+
407+
assert_ne!("", cert.stats_id);
408+
assert_eq!(expires, cert.expires());
409+
assert_eq!(pem, cert.pem());
410+
411+
Ok(())
412+
}
372413
}

src/peer_connection/configuration.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::peer_connection::policy::sdp_semantics::RTCSdpSemantics;
1010
/// Configurations may be set up once and reused across multiple connections.
1111
/// Configurations are treated as readonly. As long as they are unmodified,
1212
/// they are safe for concurrent use.
13-
#[derive(Default)]
13+
#[derive(Default, Clone)]
1414
pub struct RTCConfiguration {
1515
/// iceservers defines a slice describing servers available to be used by
1616
/// ICE, such as STUN and TURN servers.

0 commit comments

Comments
 (0)