Skip to content

Commit c72d48c

Browse files
authored
Add EC-DSA, custom certificate common names for DTLS
1 parent 5b20343 commit c72d48c

File tree

13 files changed

+416
-172
lines changed

13 files changed

+416
-172
lines changed

src/crypto/dtls.rs

+32-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use super::{CryptoError, Fingerprint, KeyingMaterial, SrtpProfile};
1515
//
1616
// Pion also sets this to "WebRTC", maybe for compatibility reasons.
1717
// https://github.com/pion/webrtc/blob/eed2bb2d3b9f204f9de1cd7e1046ca5d652778d2/constants.go#L31
18-
pub const DTLS_CERT_IDENTITY: &str = "WebRTC";
18+
const DTLS_CERT_IDENTITY: &str = "WebRTC";
1919

2020
/// Events arising from a [`Dtls`] instance.
2121
pub enum DtlsEvent {
@@ -34,6 +34,34 @@ pub enum DtlsEvent {
3434
Data(Vec<u8>),
3535
}
3636

37+
/// Defines the type of key pair to generate for the DTLS certificate.
38+
#[derive(Clone, Debug, Default)]
39+
pub enum DtlsPKeyType {
40+
/// Generate an RSA key pair
41+
Rsa2048,
42+
/// Generate an EC-DSA key pair using the NIST P-256 curve
43+
#[default]
44+
EcDsaP256,
45+
}
46+
47+
/// Controls certificate generation options.
48+
#[derive(Clone, Debug)]
49+
pub struct DtlsCertOptions {
50+
/// The common name for the certificate.
51+
pub common_name: String,
52+
/// The type of key to generate.
53+
pub pkey_type: DtlsPKeyType,
54+
}
55+
56+
impl Default for DtlsCertOptions {
57+
fn default() -> Self {
58+
Self {
59+
common_name: DTLS_CERT_IDENTITY.into(),
60+
pkey_type: Default::default(),
61+
}
62+
}
63+
}
64+
3765
/// Certificate used for DTLS.
3866
#[derive(Clone)]
3967
pub struct DtlsCert(DtlsCertInner);
@@ -59,12 +87,12 @@ impl DtlsCert {
5987
///
6088
/// * **openssl** (defaults to on) for crypto backed by OpenSSL.
6189
/// * **wincrypto** for crypto backed by windows crypto.
62-
pub fn new(p: CryptoProvider) -> Self {
90+
pub fn new(p: CryptoProvider, opts: DtlsCertOptions) -> Self {
6391
let inner = match p {
6492
CryptoProvider::OpenSsl => {
6593
#[cfg(feature = "openssl")]
6694
{
67-
let cert = super::ossl::OsslDtlsCert::new();
95+
let cert = super::ossl::OsslDtlsCert::new(opts);
6896
DtlsCertInner::OpenSsl(cert)
6997
}
7098
#[cfg(not(feature = "openssl"))]
@@ -75,7 +103,7 @@ impl DtlsCert {
75103
CryptoProvider::WinCrypto => {
76104
#[cfg(all(feature = "wincrypto", target_os = "windows"))]
77105
{
78-
let cert = super::wincrypto::WinCryptoDtlsCert::new();
106+
let cert = super::wincrypto::WinCryptoDtlsCert::new(opts);
79107
DtlsCertInner::WinCrypto(cert)
80108
}
81109
#[cfg(not(all(feature = "wincrypto", target_os = "windows")))]

src/crypto/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ mod ossl;
7676
mod wincrypto;
7777

7878
mod dtls;
79-
pub use dtls::DtlsCert;
80-
pub(crate) use dtls::{DtlsEvent, DtlsImpl};
79+
pub(crate) use dtls::DtlsImpl;
80+
pub use dtls::{DtlsCert, DtlsCertOptions, DtlsEvent, DtlsPKeyType};
8181

8282
mod finger;
8383
pub use finger::Fingerprint;

src/crypto/ossl/cert.rs

+19-8
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ use std::time::SystemTime;
22

33
use openssl::asn1::{Asn1Integer, Asn1Time, Asn1Type};
44
use openssl::bn::BigNum;
5+
use openssl::ec::{EcGroup, EcKey};
56
use openssl::hash::MessageDigest;
67
use openssl::nid::Nid;
78
use openssl::pkey::{PKey, Private};
89
use openssl::rsa::Rsa;
910
use openssl::x509::{X509Name, X509};
1011

11-
use crate::crypto::dtls::DTLS_CERT_IDENTITY;
12+
use crate::crypto::dtls::{DtlsCertOptions, DtlsPKeyType};
1213
use crate::crypto::Fingerprint;
1314

1415
use super::CryptoError;
@@ -25,16 +26,26 @@ pub struct OsslDtlsCert {
2526

2627
impl OsslDtlsCert {
2728
/// Creates a new (self signed) DTLS certificate.
28-
pub fn new() -> Self {
29-
Self::self_signed().expect("create dtls cert")
29+
pub fn new(options: DtlsCertOptions) -> Self {
30+
Self::self_signed(options).expect("create dtls cert")
3031
}
3132

3233
// The libWebRTC code we try to match is at:
3334
// https://webrtc.googlesource.com/src/+/1568f1b1330f94494197696fe235094e6293b258/rtc_base/openssl_certificate.cc#58
34-
fn self_signed() -> Result<Self, CryptoError> {
35+
fn self_signed(options: DtlsCertOptions) -> Result<Self, CryptoError> {
3536
let f4 = BigNum::from_u32(RSA_F4).unwrap();
36-
let key = Rsa::generate_with_e(2048, &f4)?;
37-
let pkey = PKey::from_rsa(key)?;
37+
let pkey = match options.pkey_type {
38+
DtlsPKeyType::Rsa2048 => {
39+
let key = Rsa::generate_with_e(2048, &f4)?;
40+
PKey::from_rsa(key)?
41+
}
42+
DtlsPKeyType::EcDsaP256 => {
43+
let nid = Nid::X9_62_PRIME256V1; // NIST P-256 curve
44+
let group = EcGroup::from_curve_name(nid)?;
45+
let key = EcKey::generate(&group)?;
46+
PKey::from_ec_key(key)?
47+
}
48+
};
3849

3950
let mut x509b = X509::builder()?;
4051
x509b.set_version(2)?; // X509.V3 (zero indexed)
@@ -64,7 +75,7 @@ impl OsslDtlsCert {
6475
let mut nameb = X509Name::builder()?;
6576
nameb.append_entry_by_nid_with_type(
6677
Nid::COMMONNAME,
67-
DTLS_CERT_IDENTITY,
78+
options.common_name.as_str(),
6879
Asn1Type::UTF8STRING,
6980
)?;
7081

@@ -73,7 +84,7 @@ impl OsslDtlsCert {
7384
x509b.set_subject_name(&name)?;
7485
x509b.set_issuer_name(&name)?;
7586

76-
x509b.sign(&pkey, MessageDigest::sha1())?;
87+
x509b.sign(&pkey, MessageDigest::sha256())?;
7788
let x509 = x509b.build();
7889

7990
Ok(OsslDtlsCert { pkey, x509 })

src/crypto/wincrypto/cert.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::CryptoError;
22
use super::WinCryptoDtls;
3-
use crate::crypto::dtls::DTLS_CERT_IDENTITY;
3+
use crate::crypto::dtls::{DtlsCertOptions, DtlsPKeyType};
44
use crate::crypto::Fingerprint;
55
use std::sync::Arc;
66
use str0m_wincrypto::WinCryptoError;
@@ -11,10 +11,18 @@ pub struct WinCryptoDtlsCert {
1111
}
1212

1313
impl WinCryptoDtlsCert {
14-
pub fn new() -> Self {
14+
pub fn new(options: DtlsCertOptions) -> Self {
15+
let use_ec_dsa_keys = match options.pkey_type {
16+
DtlsPKeyType::Rsa2048 => false,
17+
DtlsPKeyType::EcDsaP256 => true,
18+
};
19+
1520
let certificate = Arc::new(
16-
str0m_wincrypto::Certificate::new_self_signed(&format!("CN={}", DTLS_CERT_IDENTITY))
17-
.expect("Failed to create self-signed certificate"),
21+
str0m_wincrypto::Certificate::new_self_signed(
22+
use_ec_dsa_keys,
23+
&format!("CN={}", options.common_name),
24+
)
25+
.expect("Failed to create self-signed certificate"),
1826
);
1927
Self { certificate }
2028
}

src/crypto/wincrypto/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ pub use dtls::WinCryptoDtls;
1111
mod srtp;
1212
pub use srtp::WinCryptoSrtpCryptoImpl;
1313

14+
#[cfg(not(feature = "sha1"))]
1415
mod sha1;
15-
#[allow(unused_imports)] // If 'sha1' feature is enabled this is not used.
16+
#[cfg(not(feature = "sha1"))]
1617
pub use sha1::sha1_hmac;
1718

1819
pub use str0m_wincrypto::WinCryptoError;

src/dtls.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ use thiserror::Error;
55

66
use crate::crypto::{CryptoError, DtlsImpl, Fingerprint};
77

8-
pub use crate::crypto::DtlsCert;
9-
pub(crate) use crate::crypto::DtlsEvent;
8+
pub use crate::crypto::{DtlsCert, DtlsCertOptions, DtlsEvent};
109
use crate::net::DatagramSend;
1110

1211
/// Errors that can arise in DTLS.

src/lib.rs

+56-37
Original file line numberDiff line numberDiff line change
@@ -626,8 +626,7 @@ use crypto::CryptoProvider;
626626
use crypto::Fingerprint;
627627

628628
mod dtls;
629-
use dtls::DtlsCert;
630-
use dtls::{Dtls, DtlsEvent};
629+
use dtls::{Dtls, DtlsCert, DtlsCertOptions, DtlsEvent};
631630

632631
#[path = "ice/mod.rs"]
633632
mod ice_;
@@ -637,7 +636,7 @@ pub use ice_::{Candidate, CandidateKind, IceConnectionState, IceCreds};
637636

638637
/// Additional configuration.
639638
pub mod config {
640-
pub use super::crypto::{CryptoProvider, DtlsCert, Fingerprint};
639+
pub use super::crypto::{CryptoProvider, DtlsCert, DtlsCertOptions, DtlsPKeyType, Fingerprint};
641640
}
642641

643642
/// Low level ICE access.
@@ -1141,10 +1140,9 @@ impl Rtc {
11411140
ice.set_ice_lite(config.ice_lite);
11421141
}
11431142

1144-
let dtls_cert = if let Some(c) = config.dtls_cert {
1145-
c
1146-
} else {
1147-
DtlsCert::new(config.crypto_provider)
1143+
let dtls_cert = match config.dtls_cert_config {
1144+
DtlsCertConfig::Options(options) => DtlsCert::new(config.crypto_provider, options),
1145+
DtlsCertConfig::PregeneratedCert(cert) => cert,
11481146
};
11491147

11501148
let crypto_provider = dtls_cert.crypto_provider();
@@ -1854,6 +1852,25 @@ impl Rtc {
18541852
}
18551853
}
18561854

1855+
/// Configuation for the DTLS certificate used for the Rtc instance. This can be set to
1856+
/// allow a pregenerated certificate, or options to pass when generating a certificate
1857+
/// on-the-fly.
1858+
///
1859+
/// The default value is DtlsCertConfig::Options(DtlsCertOptions::default())
1860+
#[derive(Clone, Debug)]
1861+
pub enum DtlsCertConfig {
1862+
/// The options to use for the DTLS certificate generated for this Rtc instance.
1863+
Options(DtlsCertOptions),
1864+
/// A pregenerated certificate to use for this Rtc instance.
1865+
PregeneratedCert(DtlsCert),
1866+
}
1867+
1868+
impl Default for DtlsCertConfig {
1869+
fn default() -> Self {
1870+
DtlsCertConfig::Options(DtlsCertOptions::default())
1871+
}
1872+
}
1873+
18571874
/// Customized config for creating an [`Rtc`] instance.
18581875
///
18591876
/// ```
@@ -1871,7 +1888,7 @@ impl Rtc {
18711888
pub struct RtcConfig {
18721889
local_ice_credentials: Option<IceCreds>,
18731890
crypto_provider: CryptoProvider,
1874-
dtls_cert: Option<DtlsCert>,
1891+
dtls_cert_config: DtlsCertConfig,
18751892
fingerprint_verification: bool,
18761893
ice_lite: bool,
18771894
codec_config: CodecConfig,
@@ -1921,7 +1938,7 @@ impl RtcConfig {
19211938
///
19221939
/// This overrides what is set in [`CryptoProvider::install_process_default()`].
19231940
pub fn set_crypto_provider(mut self, p: CryptoProvider) -> Self {
1924-
if let Some(c) = &self.dtls_cert {
1941+
if let DtlsCertConfig::PregeneratedCert(c) = &self.dtls_cert_config {
19251942
if p != c.crypto_provider() {
19261943
panic!("set_dtls_cert() locked crypto provider to: {}", p);
19271944
}
@@ -1939,46 +1956,48 @@ impl RtcConfig {
19391956
self.crypto_provider
19401957
}
19411958

1942-
/// Get the configured DTLS certificate, if set.
1943-
///
1944-
/// Returns [`None`] if no DTLS certificate is set. In such cases,
1945-
/// the certificate will be created on build and you can use the
1946-
/// direct API on an [`Rtc`] instance to obtain the local
1947-
/// DTLS fingerprint.
1959+
/// Returns the configured DTLS certificate configuration.
19481960
///
1961+
/// Defaults to a configuration similar to libwebrtc:
19491962
/// ```
1950-
/// # #[cfg(feature = "openssl")] {
1951-
/// # use str0m::RtcConfig;
1952-
/// let fingerprint = RtcConfig::default()
1953-
/// .build()
1954-
/// .direct_api()
1955-
/// .local_dtls_fingerprint();
1956-
/// # }
1963+
/// # use str0m::DtlsCertConfig;
1964+
/// # use str0m::config::{DtlsCertOptions, DtlsPKeyType};
1965+
///
1966+
/// DtlsCertConfig::Options(DtlsCertOptions {
1967+
/// common_name: "WebRTC".into(),
1968+
/// pkey_type: DtlsPKeyType::EcDsaP256,
1969+
/// });
19571970
/// ```
1958-
pub fn dtls_cert(&self) -> Option<&DtlsCert> {
1959-
self.dtls_cert.as_ref()
1971+
pub fn dtls_cert_config(&self) -> &DtlsCertConfig {
1972+
&self.dtls_cert_config
19601973
}
19611974

1962-
/// Set the DTLS certificate for secure communication.
1975+
/// Set the DTLS certificate configuration for certificate generation.
19631976
///
1964-
/// Generating a certificate can be a time-consuming process.
1965-
/// Use this API to reuse a previously created [`DtlsCert`] if available.
1977+
/// Setting this permits you to assign a Pregenerated certificate, or
1978+
/// options for certificate generation, such as signing key type, and
1979+
/// subject name.
19661980
///
1967-
/// Setting this locks the `crypto_provider()` setting to the [`CryptoProvider`],
1968-
/// for the DTLS certificate.
1981+
/// If a Pregenerated certificate is set, this locks the `crypto_provider()`
1982+
/// setting to the [`CryptoProvider`], for the DTLS certificate.
19691983
///
19701984
/// ```
1971-
/// # use str0m::RtcConfig;
1972-
/// # use str0m::config::{DtlsCert, CryptoProvider};
1985+
/// # use str0m::{DtlsCertConfig, RtcConfig};
1986+
/// # use str0m::config::{DtlsCertOptions, DtlsPKeyType};
19731987
///
1974-
/// let dtls_cert = DtlsCert::new(CryptoProvider::OpenSsl);
1988+
/// let dtls_cert_config = DtlsCertConfig::Options(DtlsCertOptions {
1989+
/// common_name: "Clark Kent".into(),
1990+
/// pkey_type: DtlsPKeyType::EcDsaP256,
1991+
/// });
19751992
///
19761993
/// let rtc_config = RtcConfig::default()
1977-
/// .set_dtls_cert(dtls_cert);
1994+
/// .set_dtls_cert_config(dtls_cert_config);
19781995
/// ```
1979-
pub fn set_dtls_cert(mut self, dtls_cert: DtlsCert) -> Self {
1980-
self.crypto_provider = dtls_cert.crypto_provider();
1981-
self.dtls_cert = Some(dtls_cert);
1996+
pub fn set_dtls_cert_config(mut self, dtls_cert_config: DtlsCertConfig) -> Self {
1997+
if let DtlsCertConfig::PregeneratedCert(ref cert) = dtls_cert_config {
1998+
self.crypto_provider = cert.crypto_provider();
1999+
}
2000+
self.dtls_cert_config = dtls_cert_config;
19822001
self
19832002
}
19842003

@@ -2388,7 +2407,7 @@ impl Default for RtcConfig {
23882407
Self {
23892408
local_ice_credentials: None,
23902409
crypto_provider: CryptoProvider::process_default().unwrap_or(CryptoProvider::OpenSsl),
2391-
dtls_cert: None,
2410+
dtls_cert_config: Default::default(),
23922411
fingerprint_verification: true,
23932412
ice_lite: false,
23942413
codec_config: CodecConfig::new_with_defaults(),

wincrypto/Cargo.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wincrypto/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ license = "MIT OR Apache-2.0"
99
[dependencies]
1010
thiserror = { version = "1.0.38" }
1111
tracing = "0.1.37"
12-
windows = { version = "0.58", features=["Win32_Security_Cryptography", "Win32_Security_Authentication_Identity", "Win32_Security_Credentials",]}
12+
windows = { version = "0.58", features=["Win32_Security_Cryptography", "Win32_Security_Authentication_Identity", "Win32_Security_Credentials", "Win32_System_Rpc",]}

0 commit comments

Comments
 (0)