str0m/crypto/
dtls.rs

1#![allow(unreachable_patterns, dead_code, unused_variables)]
2
3use std::collections::VecDeque;
4use std::fmt;
5use std::time::Instant;
6
7use crate::net::DatagramSend;
8
9use super::CryptoProvider;
10use super::{CryptoError, Fingerprint, KeyingMaterial, SrtpProfile};
11
12// libWebRTC says "WebRTC" here when doing OpenSSL, for BoringSSL they seem
13// to generate a random 8 characters.
14// https://webrtc.googlesource.com/src/+/1568f1b1330f94494197696fe235094e6293b258/rtc_base/rtc_certificate_generator.cc#27
15//
16// Pion also sets this to "WebRTC", maybe for compatibility reasons.
17// https://github.com/pion/webrtc/blob/eed2bb2d3b9f204f9de1cd7e1046ca5d652778d2/constants.go#L31
18const DTLS_CERT_IDENTITY: &str = "WebRTC";
19
20/// Events arising from a [`Dtls`] instance.
21pub enum DtlsEvent {
22    /// When the DTLS has finished handshaking.
23    Connected,
24
25    /// Keying material for SRTP encryption master key and the selected SRTP profile.
26    SrtpKeyingMaterial(KeyingMaterial, SrtpProfile),
27
28    /// The fingerprint of the remote peer.
29    ///
30    /// This should be checked against the fingerprint communicated in the SDP.
31    RemoteFingerprint(Fingerprint),
32
33    /// Decrypted data from incoming DTLS traffic.
34    Data(Vec<u8>),
35}
36
37/// Defines the type of key pair to generate for the DTLS certificate.
38#[derive(Clone, Debug, Default)]
39pub 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)]
49pub 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
56impl 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
65/// Certificate used for DTLS.
66#[derive(Clone)]
67pub struct DtlsCert(DtlsCertInner);
68
69#[derive(Debug, Clone)]
70enum DtlsCertInner {
71    #[cfg(feature = "openssl")]
72    OpenSsl(super::ossl::OsslDtlsCert),
73    #[cfg(not(feature = "openssl"))]
74    OpenSsl(DummyCert),
75    #[cfg(all(feature = "wincrypto", target_os = "windows"))]
76    WinCrypto(super::wincrypto::WinCryptoDtlsCert),
77    #[cfg(not(all(feature = "wincrypto", target_os = "windows")))]
78    WinCrypto(DummyCert),
79}
80
81impl DtlsCert {
82    /// Creates a new DTLS certificate.
83    ///
84    /// The certificate is bound to an actual crypto implementation. Pass the
85    /// desired provider. The provider implementations will need turning on
86    /// using the feature flags:
87    ///
88    /// * **openssl** (defaults to on) for crypto backed by OpenSSL.
89    /// * **wincrypto** for crypto backed by windows crypto.
90    pub fn new(p: CryptoProvider, opts: DtlsCertOptions) -> Self {
91        let inner = match p {
92            CryptoProvider::OpenSsl => {
93                #[cfg(feature = "openssl")]
94                {
95                    let cert = super::ossl::OsslDtlsCert::new(opts);
96                    DtlsCertInner::OpenSsl(cert)
97                }
98                #[cfg(not(feature = "openssl"))]
99                {
100                    DtlsCertInner::OpenSsl(DummyCert(p))
101                }
102            }
103            CryptoProvider::WinCrypto => {
104                #[cfg(all(feature = "wincrypto", target_os = "windows"))]
105                {
106                    let cert = super::wincrypto::WinCryptoDtlsCert::new(opts);
107                    DtlsCertInner::WinCrypto(cert)
108                }
109                #[cfg(not(all(feature = "wincrypto", target_os = "windows")))]
110                {
111                    DtlsCertInner::WinCrypto(DummyCert(p))
112                }
113            }
114        };
115
116        DtlsCert(inner)
117    }
118
119    pub(crate) fn crypto_provider(&self) -> CryptoProvider {
120        match self.0 {
121            DtlsCertInner::OpenSsl(_) => CryptoProvider::OpenSsl,
122            DtlsCertInner::WinCrypto(_) => CryptoProvider::WinCrypto,
123        }
124    }
125
126    /// Creates a fingerprint for this certificate.
127    ///
128    /// Fingerprints are used to verify a remote peer's certificate.
129    pub fn fingerprint(&self) -> Fingerprint {
130        match &self.0 {
131            DtlsCertInner::OpenSsl(v) => v.fingerprint(),
132            DtlsCertInner::WinCrypto(v) => v.fingerprint(),
133            _ => unreachable!(),
134        }
135    }
136
137    pub(crate) fn create_dtls_impl(&self) -> Result<DtlsImpl, CryptoError> {
138        let imp = match &self.0 {
139            DtlsCertInner::OpenSsl(v) => DtlsImpl::OpenSsl(v.new_dtls_impl()?),
140            DtlsCertInner::WinCrypto(v) => DtlsImpl::WinCrypto(v.new_dtls_impl()?),
141        };
142
143        Ok(imp)
144    }
145}
146
147impl fmt::Debug for DtlsCert {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        match &self.0 {
150            DtlsCertInner::OpenSsl(c) => c.fmt(f),
151            DtlsCertInner::WinCrypto(c) => c.fmt(f),
152            _ => unreachable!(),
153        }
154    }
155}
156
157pub trait DtlsInner: Sized {
158    /// Set whether this instance is active or passive.
159    ///
160    /// i.e. initiating the client hello or not. This must be called
161    /// exactly once before starting to handshake (I/O).
162    fn set_active(&mut self, active: bool);
163
164    /// Handle the handshake. Once this succeeds, it becomes a no-op.
165    fn handle_handshake(&mut self, o: &mut VecDeque<DtlsEvent>) -> Result<bool, CryptoError>;
166
167    /// If set_active, returns what was set.
168    fn is_active(&self) -> Option<bool>;
169
170    /// Handles an incoming DTLS datagrams.
171    fn handle_receive(&mut self, m: &[u8], o: &mut VecDeque<DtlsEvent>) -> Result<(), CryptoError>;
172
173    /// Poll for the next datagram to send.
174    fn poll_datagram(&mut self) -> Option<DatagramSend>;
175
176    /// Poll for next timeout. This is only used during DTLS handshake.
177    fn poll_timeout(&mut self, now: Instant) -> Option<Instant>;
178
179    /// Handling incoming data to be sent as DTLS datagrams.
180    fn handle_input(&mut self, data: &[u8]) -> Result<(), CryptoError>;
181
182    /// Whether the DTLS connection is established.
183    fn is_connected(&self) -> bool;
184}
185
186pub(crate) enum DtlsImpl {
187    #[cfg(feature = "openssl")]
188    OpenSsl(super::ossl::OsslDtlsImpl),
189    #[cfg(not(feature = "openssl"))]
190    OpenSsl(DummyDtlsImpl),
191    #[cfg(all(feature = "wincrypto", target_os = "windows"))]
192    WinCrypto(super::wincrypto::WinCryptoDtls),
193    #[cfg(not(all(feature = "wincrypto", target_os = "windows")))]
194    WinCrypto(DummyDtlsImpl),
195}
196
197impl DtlsImpl {
198    pub fn set_active(&mut self, active: bool) {
199        match self {
200            DtlsImpl::OpenSsl(v) => v.set_active(active),
201            DtlsImpl::WinCrypto(v) => v.set_active(active),
202        }
203    }
204
205    pub fn handle_handshake(&mut self, o: &mut VecDeque<DtlsEvent>) -> Result<bool, CryptoError> {
206        match self {
207            DtlsImpl::OpenSsl(i) => i.handle_handshake(o),
208            DtlsImpl::WinCrypto(i) => i.handle_handshake(o),
209        }
210    }
211
212    pub fn is_active(&self) -> Option<bool> {
213        match self {
214            DtlsImpl::OpenSsl(i) => i.is_active(),
215            DtlsImpl::WinCrypto(i) => i.is_active(),
216        }
217    }
218
219    pub fn handle_receive(
220        &mut self,
221        m: &[u8],
222        o: &mut VecDeque<DtlsEvent>,
223    ) -> Result<(), CryptoError> {
224        match self {
225            DtlsImpl::OpenSsl(i) => i.handle_receive(m, o),
226            DtlsImpl::WinCrypto(i) => i.handle_receive(m, o),
227        }
228    }
229
230    pub fn poll_datagram(&mut self) -> Option<DatagramSend> {
231        match self {
232            DtlsImpl::OpenSsl(i) => i.poll_datagram(),
233            DtlsImpl::WinCrypto(i) => i.poll_datagram(),
234        }
235    }
236
237    pub fn poll_timeout(&mut self, now: Instant) -> Option<Instant> {
238        match self {
239            DtlsImpl::OpenSsl(i) => i.poll_timeout(now),
240            DtlsImpl::WinCrypto(i) => i.poll_timeout(now),
241        }
242    }
243
244    pub fn handle_input(&mut self, data: &[u8]) -> Result<(), CryptoError> {
245        match self {
246            DtlsImpl::OpenSsl(i) => i.handle_input(data),
247            DtlsImpl::WinCrypto(i) => i.handle_input(data),
248        }
249    }
250
251    pub fn is_connected(&self) -> bool {
252        match self {
253            DtlsImpl::OpenSsl(i) => i.is_connected(),
254            DtlsImpl::WinCrypto(i) => i.is_connected(),
255        }
256    }
257}
258
259#[derive(Debug, Clone)]
260struct DummyCert(CryptoProvider);
261
262impl DummyCert {
263    fn fingerprint(&self) -> Fingerprint {
264        panic!("Must enable feature: {}", self.0)
265    }
266
267    fn new_dtls_impl(&self) -> Result<DummyDtlsImpl, CryptoError> {
268        panic!("Must enable feature: {}", self.0)
269    }
270}
271
272pub struct DummyDtlsImpl(CryptoProvider);
273
274impl DummyDtlsImpl {
275    fn set_active(&self, active: bool) {
276        panic!("Must enable feature: {}", self.0)
277    }
278
279    fn handle_handshake(&self, o: &mut VecDeque<DtlsEvent>) -> Result<bool, CryptoError> {
280        panic!("Must enable feature: {}", self.0)
281    }
282
283    fn is_active(&self) -> Option<bool> {
284        panic!("Must enable feature: {}", self.0)
285    }
286
287    fn handle_receive(&self, m: &[u8], o: &mut VecDeque<DtlsEvent>) -> Result<(), CryptoError> {
288        panic!("Must enable feature: {}", self.0)
289    }
290
291    fn poll_datagram(&self) -> Option<DatagramSend> {
292        panic!("Must enable feature: {}", self.0)
293    }
294
295    fn poll_timeout(&self, now: Instant) -> Option<Instant> {
296        panic!("Must enable feature: {}", self.0)
297    }
298
299    fn handle_input(&self, data: &[u8]) -> Result<(), CryptoError> {
300        panic!("Must enable feature: {}", self.0)
301    }
302
303    fn is_connected(&self) -> bool {
304        panic!("Must enable feature: {}", self.0)
305    }
306}