Skip to main content

str0m_proto/crypto/
provider.rs

1//! Cryptographic provider traits for pluggable crypto backends.
2//!
3//! This module defines the trait-based interface for cryptographic operations
4//! in str0m, allowing users to provide custom crypto implementations for SRTP,
5//! SHA1 HMAC (for STUN), and DTLS.
6//!
7//! Implementors of a crypto provider only need to depend on this module.
8
9use std::fmt;
10use std::fmt::Debug;
11use std::panic::{RefUnwindSafe, UnwindSafe};
12use std::sync::OnceLock;
13use std::time::Instant;
14
15use subtle::ConstantTimeEq;
16
17use crate::crypto::dtls::*;
18use crate::crypto::error::CryptoError;
19
20// ============================================================================
21// CryptoProvider
22// ============================================================================
23
24/// Cryptographic provider for SRTP, SHA1 HMAC, and DTLS operations.
25///
26/// This struct holds references to all cryptographic components needed
27/// for WebRTC operations. Users can provide custom implementations of each component
28/// to replace the default OpenSSL-based provider.
29///
30/// # Design
31///
32/// The provider uses static trait object references (`&'static dyn Trait`) which
33/// provides zero runtime overhead for trait dispatch. This design is inspired by
34/// dimpl's CryptoProvider and ensures efficient crypto operations.
35#[derive(Debug, Clone)]
36pub struct CryptoProvider {
37    /// SRTP provider for creating cipher instances and key derivation.
38    pub srtp_provider: &'static dyn SrtpProvider,
39    /// SHA1 HMAC provider for STUN message integrity.
40    pub sha1_hmac_provider: &'static dyn Sha1HmacProvider,
41    /// SHA-256 hash provider.
42    pub sha256_provider: &'static dyn Sha256Provider,
43    /// DTLS provider for creating DTLS instances.
44    pub dtls_provider: &'static dyn DtlsProvider,
45}
46
47/// CryptoProvider contains only static references to thread-safe traits,
48/// so it's safe to use across panic boundaries.
49impl UnwindSafe for CryptoProvider {}
50impl RefUnwindSafe for CryptoProvider {}
51
52/// Static storage for the default crypto provider.
53static DEFAULT: OnceLock<CryptoProvider> = OnceLock::new();
54
55impl CryptoProvider {
56    /// Install this provider as the process-wide default.
57    pub fn install_process_default(self) {
58        let _ = DEFAULT.set(self);
59    }
60
61    /// Install a default crypto provider for the process.
62    ///
63    /// # Panics
64    ///
65    /// Panics if called more than once.
66    pub fn install_default(provider: CryptoProvider) {
67        DEFAULT
68            .set(provider)
69            .expect("CryptoProvider::install_default() called more than once");
70    }
71
72    /// Get the default crypto provider, if one has been installed.
73    pub fn get_default() -> Option<&'static CryptoProvider> {
74        DEFAULT.get()
75    }
76}
77
78impl fmt::Display for CryptoProvider {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        write!(f, "CryptoProvider")
81    }
82}
83
84// ============================================================================
85// Marker Trait
86// ============================================================================
87
88/// Marker trait for types that are safe to use in crypto provider components.
89///
90/// This trait combines the common bounds required for crypto provider trait objects:
91/// - [`Send`] + [`Sync`]: Thread-safe
92/// - [`Debug`]: Support debugging
93///
94/// Note: We don't require `UnwindSafe` because some error types (like `dimpl::Error`)
95/// may not implement it, but they're still safe to use in our context.
96pub trait CryptoSafe: Send + Sync + Debug {}
97
98/// Blanket implementation: any type satisfying the bounds implements [`CryptoSafe`].
99impl<T: Send + Sync + Debug> CryptoSafe for T {}
100
101// ============================================================================
102// Main Provider Traits
103// ============================================================================
104
105/// SRTP provider for creating cipher instances and key derivation.
106pub trait SrtpProvider: CryptoSafe {
107    /// Factory for AES-128-CM-SHA1-80 ciphers.
108    fn aes_128_cm_sha1_80(&self) -> &'static dyn SupportedAes128CmSha1_80;
109
110    /// Factory for AEAD-AES-128-GCM ciphers.
111    fn aead_aes_128_gcm(&self) -> &'static dyn SupportedAeadAes128Gcm;
112
113    /// Factory for AEAD-AES-256-GCM ciphers.
114    fn aead_aes_256_gcm(&self) -> &'static dyn SupportedAeadAes256Gcm;
115
116    /// Perform AES-128-ECB round for key derivation.
117    fn srtp_aes_128_ecb_round(&self, key: &[u8], input: &[u8], output: &mut [u8]);
118
119    /// Perform AES-256-ECB round for key derivation.
120    fn srtp_aes_256_ecb_round(&self, key: &[u8], input: &[u8], output: &mut [u8]);
121}
122
123/// SHA1 HMAC provider for STUN message integrity.
124pub trait Sha1HmacProvider: CryptoSafe {
125    /// Compute HMAC-SHA1(key, payloads) and return the result.
126    fn sha1_hmac(&self, key: &[u8], payloads: &[&[u8]]) -> [u8; 20];
127}
128
129/// SHA-256 hash provider.
130pub trait Sha256Provider: CryptoSafe {
131    /// Compute SHA-256 hash of the input data.
132    fn sha256(&self, data: &[u8]) -> [u8; 32];
133}
134
135/// Factory for DTLS instances and certificates.
136pub trait DtlsProvider: CryptoSafe {
137    /// Generate a new self-signed DTLS certificate.
138    ///
139    /// Returns `None` if this provider does not support certificate generation.
140    /// In that case, the user must supply a certificate via `RtcConfig::set_dtls_cert`.
141    fn generate_certificate(&self) -> Option<DtlsCert>;
142
143    /// Create a new DTLS instance with the given certificate.
144    fn new_dtls(&self, cert: &DtlsCert) -> Result<Box<dyn DtlsInstance>, CryptoError>;
145
146    /// Whether the provider is used in a test context.
147    fn is_test(&self) -> bool {
148        cfg!(feature = "_internal_test_exports")
149    }
150}
151
152// ============================================================================
153// DTLS Instance Trait
154// ============================================================================
155
156/// DTLS instance operations (matches dimpl's Dtls API surface).
157pub trait DtlsInstance: CryptoSafe {
158    /// Set whether this instance is active (client) or passive (server).
159    fn set_active(&mut self, active: bool);
160
161    /// Handle an incoming DTLS packet.
162    fn handle_packet(&mut self, packet: &[u8]) -> Result<(), DtlsImplError>;
163
164    /// Poll for output from the DTLS instance.
165    ///
166    /// The buffer must be large enough to hold the largest possible DTLS record.
167    fn poll_output<'a>(&mut self, buf: &'a mut [u8]) -> DtlsOutput<'a>;
168
169    /// Handle a timeout event.
170    fn handle_timeout(&mut self, now: Instant) -> Result<(), DtlsImplError>;
171
172    /// Send application data over DTLS.
173    fn send_application_data(&mut self, data: &[u8]) -> Result<(), DtlsImplError>;
174
175    /// Return true if the instance is operating in the client role.
176    fn is_active(&self) -> bool;
177}
178
179// ============================================================================
180// SRTP Cipher Factory Traits
181// ============================================================================
182
183/// Factory for AES-128-CM-SHA1-80 cipher instances.
184pub trait SupportedAes128CmSha1_80: CryptoSafe {
185    /// Create a cipher instance with the given key.
186    fn create_cipher(&self, key: [u8; 16], encrypt: bool) -> Box<dyn Aes128CmSha1_80Cipher>;
187}
188
189/// Factory for AEAD-AES-128-GCM cipher instances.
190pub trait SupportedAeadAes128Gcm: CryptoSafe {
191    /// Create a cipher instance with the given key.
192    fn create_cipher(&self, key: [u8; 16], encrypt: bool) -> Box<dyn AeadAes128GcmCipher>;
193}
194
195/// Factory for AEAD-AES-256-GCM cipher instances.
196pub trait SupportedAeadAes256Gcm: CryptoSafe {
197    /// Create a cipher instance with the given key.
198    fn create_cipher(&self, key: [u8; 32], encrypt: bool) -> Box<dyn AeadAes256GcmCipher>;
199}
200
201// ============================================================================
202// SRTP Cipher Instance Traits
203// ============================================================================
204
205/// AES-128-CM-SHA1-80 cipher instance for SRTP encryption/decryption.
206pub trait Aes128CmSha1_80Cipher: CryptoSafe {
207    /// Encrypt input with the given IV.
208    fn encrypt(
209        &mut self,
210        iv: &[u8; 16],
211        input: &[u8],
212        output: &mut [u8],
213    ) -> Result<(), CryptoError>;
214
215    /// Decrypt input with the given IV.
216    fn decrypt(
217        &mut self,
218        iv: &[u8; 16],
219        input: &[u8],
220        output: &mut [u8],
221    ) -> Result<(), CryptoError>;
222}
223
224/// AEAD-AES-128-GCM cipher instance for SRTP encryption/decryption.
225pub trait AeadAes128GcmCipher: CryptoSafe {
226    /// Encrypt input with the given IV and AAD.
227    fn encrypt(
228        &mut self,
229        iv: &[u8; 12],
230        aad: &[u8],
231        input: &[u8],
232        output: &mut [u8],
233    ) -> Result<(), CryptoError>;
234
235    /// Decrypt input with the given IV and AADs.
236    fn decrypt(
237        &mut self,
238        iv: &[u8; 12],
239        aads: &[&[u8]],
240        input: &[u8],
241        output: &mut [u8],
242    ) -> Result<usize, CryptoError>;
243}
244
245/// AEAD-AES-256-GCM cipher instance for SRTP encryption/decryption.
246pub trait AeadAes256GcmCipher: CryptoSafe {
247    /// Encrypt input with the given IV and AAD.
248    fn encrypt(
249        &mut self,
250        iv: &[u8; 12],
251        aad: &[u8],
252        input: &[u8],
253        output: &mut [u8],
254    ) -> Result<(), CryptoError>;
255
256    /// Decrypt input with the given IV and AADs.
257    fn decrypt(
258        &mut self,
259        iv: &[u8; 12],
260        aads: &[&[u8]],
261        input: &[u8],
262        output: &mut [u8],
263    ) -> Result<usize, CryptoError>;
264}
265
266// ============================================================================
267// SRTP Profile Definitions
268// ============================================================================
269
270/// AES-128-CM-SHA1-80 SRTP profile.
271///
272/// This struct provides constants, types, and protocol functions for the
273/// AES-128-CM-SHA1-80 SRTP protection profile (RFC 5764).
274#[derive(Debug, Clone, Copy)]
275pub struct Aes128CmSha1_80;
276
277impl Aes128CmSha1_80 {
278    /// Key length in bytes.
279    pub const KEY_LEN: usize = 16;
280    /// Salt length in bytes.
281    pub const SALT_LEN: usize = 14;
282    /// HMAC key length in bytes.
283    pub const HMAC_KEY_LEN: usize = 20;
284    /// HMAC tag length in bytes.
285    pub const HMAC_TAG_LEN: usize = 10;
286
287    /// Compute and append RTP HMAC tag.
288    pub fn rtp_hmac(
289        sha1_hmac: impl Fn(&[u8], &[&[u8]]) -> [u8; 20],
290        key: &[u8],
291        buf: &mut [u8],
292        srtp_index: u64,
293        hmac_start: usize,
294    ) {
295        let roc = (srtp_index >> 16) as u32;
296        let tag = sha1_hmac(key, &[&buf[..hmac_start], &roc.to_be_bytes()]);
297        buf[hmac_start..(hmac_start + Self::HMAC_TAG_LEN)]
298            .copy_from_slice(&tag[0..Self::HMAC_TAG_LEN]);
299    }
300
301    /// Verify RTP HMAC tag.
302    pub fn rtp_verify(
303        sha1_hmac: impl Fn(&[u8], &[&[u8]]) -> [u8; 20],
304        key: &[u8],
305        buf: &[u8],
306        srtp_index: u64,
307        cmp: &[u8],
308    ) -> bool {
309        let roc = (srtp_index >> 16) as u32;
310        let tag = sha1_hmac(key, &[buf, &roc.to_be_bytes()]);
311        tag[0..Self::HMAC_TAG_LEN].ct_eq(cmp).into()
312    }
313
314    /// Compute RTP IV.
315    pub fn rtp_iv(salt: [u8; 14], ssrc: u32, srtp_index: u64) -> [u8; 16] {
316        let mut iv = [0; 16];
317        let ssrc_be = ssrc.to_be_bytes();
318        let srtp_be = srtp_index.to_be_bytes();
319        iv[4..8].copy_from_slice(&ssrc_be);
320        for i in 0..8 {
321            iv[i + 6] ^= srtp_be[i];
322        }
323        for i in 0..14 {
324            iv[i] ^= salt[i];
325        }
326        iv
327    }
328
329    /// Compute and append RTCP HMAC tag.
330    pub fn rtcp_hmac(
331        sha1_hmac: impl Fn(&[u8], &[&[u8]]) -> [u8; 20],
332        key: &[u8],
333        buf: &mut [u8],
334        hmac_index: usize,
335    ) {
336        let tag = sha1_hmac(key, &[&buf[0..hmac_index]]);
337        buf[hmac_index..(hmac_index + Self::HMAC_TAG_LEN)]
338            .copy_from_slice(&tag[0..Self::HMAC_TAG_LEN]);
339    }
340
341    /// Verify RTCP HMAC tag.
342    pub fn rtcp_verify(
343        sha1_hmac: impl Fn(&[u8], &[&[u8]]) -> [u8; 20],
344        key: &[u8],
345        buf: &[u8],
346        cmp: &[u8],
347    ) -> bool {
348        let tag = sha1_hmac(key, &[buf]);
349        tag[0..Self::HMAC_TAG_LEN].ct_eq(cmp).into()
350    }
351}
352
353/// AEAD-AES-128-GCM SRTP profile.
354///
355/// This struct provides constants, types, and protocol functions for the
356/// AEAD-AES-128-GCM SRTP protection profile (RFC 7714).
357#[derive(Debug, Clone, Copy)]
358pub struct AeadAes128Gcm;
359
360impl AeadAes128Gcm {
361    /// Key length in bytes.
362    pub const KEY_LEN: usize = 16;
363    /// Salt length in bytes.
364    pub const SALT_LEN: usize = 12;
365    /// RTCP AAD length in bytes.
366    pub const RTCP_AAD_LEN: usize = 12;
367    /// Authentication tag length in bytes.
368    pub const TAG_LEN: usize = 16;
369    /// IV length in bytes.
370    pub const IV_LEN: usize = 12;
371
372    /// Compute RTP IV.
373    pub fn rtp_iv(salt: [u8; 12], ssrc: u32, roc: u32, seq: u16) -> [u8; 12] {
374        // See: https://www.rfc-editor.org/rfc/rfc7714#section-8.1
375        let mut iv = [0; 12];
376
377        let ssrc_be = ssrc.to_be_bytes();
378        let roc_be = roc.to_be_bytes();
379        let seq_be = seq.to_be_bytes();
380
381        iv[2..6].copy_from_slice(&ssrc_be);
382        iv[6..10].copy_from_slice(&roc_be);
383        iv[10..12].copy_from_slice(&seq_be);
384
385        for i in 0..12 {
386            iv[i] ^= salt[i];
387        }
388
389        iv
390    }
391
392    /// Compute RTCP IV.
393    pub fn rtcp_iv(salt: [u8; 12], ssrc: u32, srtp_index: u32) -> [u8; 12] {
394        // See: https://www.rfc-editor.org/rfc/rfc7714#section-9.1
395        let mut iv = [0; 12];
396
397        let ssrc_be = ssrc.to_be_bytes();
398        let srtp_be = srtp_index.to_be_bytes();
399
400        iv[2..6].copy_from_slice(&ssrc_be);
401        iv[8..12].copy_from_slice(&srtp_be);
402
403        for i in 0..12 {
404            iv[i] ^= salt[i];
405        }
406
407        iv
408    }
409}
410
411/// AEAD-AES-256-GCM SRTP profile.
412///
413/// This struct provides constants, types, and protocol functions for the
414/// AEAD-AES-256-GCM SRTP protection profile (RFC 7714).
415#[derive(Debug, Clone, Copy)]
416pub struct AeadAes256Gcm;
417
418impl AeadAes256Gcm {
419    /// Key length in bytes.
420    pub const KEY_LEN: usize = 32;
421    /// Salt length in bytes.
422    pub const SALT_LEN: usize = 12;
423    /// RTCP AAD length in bytes.
424    pub const RTCP_AAD_LEN: usize = 12;
425    /// Authentication tag length in bytes.
426    pub const TAG_LEN: usize = 16;
427    /// IV length in bytes.
428    pub const IV_LEN: usize = 12;
429
430    /// Compute RTP IV.
431    pub fn rtp_iv(salt: [u8; 12], ssrc: u32, roc: u32, seq: u16) -> [u8; 12] {
432        // See: https://www.rfc-editor.org/rfc/rfc7714#section-8.1
433        let mut iv = [0; 12];
434
435        let ssrc_be = ssrc.to_be_bytes();
436        let roc_be = roc.to_be_bytes();
437        let seq_be = seq.to_be_bytes();
438
439        iv[2..6].copy_from_slice(&ssrc_be);
440        iv[6..10].copy_from_slice(&roc_be);
441        iv[10..12].copy_from_slice(&seq_be);
442
443        for i in 0..12 {
444            iv[i] ^= salt[i];
445        }
446
447        iv
448    }
449
450    /// Compute RTCP IV.
451    pub fn rtcp_iv(salt: [u8; 12], ssrc: u32, srtp_index: u32) -> [u8; 12] {
452        // See: https://www.rfc-editor.org/rfc/rfc7714#section-9.1
453        let mut iv = [0; 12];
454
455        let ssrc_be = ssrc.to_be_bytes();
456        let srtp_be = srtp_index.to_be_bytes();
457
458        iv[2..6].copy_from_slice(&ssrc_be);
459        iv[8..12].copy_from_slice(&srtp_be);
460
461        for i in 0..12 {
462            iv[i] ^= salt[i];
463        }
464
465        iv
466    }
467}