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
147// ============================================================================
148// DTLS Instance Trait
149// ============================================================================
150
151/// DTLS instance operations (matches dimpl's Dtls API surface).
152pub trait DtlsInstance: CryptoSafe {
153    /// Set whether this instance is active (client) or passive (server).
154    fn set_active(&mut self, active: bool);
155
156    /// Handle an incoming DTLS packet.
157    fn handle_packet(&mut self, packet: &[u8]) -> Result<(), DtlsImplError>;
158
159    /// Poll for output from the DTLS instance.
160    ///
161    /// The buffer must be large enough to hold the largest possible DTLS record.
162    fn poll_output<'a>(&mut self, buf: &'a mut [u8]) -> DtlsOutput<'a>;
163
164    /// Handle a timeout event.
165    fn handle_timeout(&mut self, now: Instant) -> Result<(), DtlsImplError>;
166
167    /// Send application data over DTLS.
168    fn send_application_data(&mut self, data: &[u8]) -> Result<(), DtlsImplError>;
169
170    /// Return true if the instance is operating in the client role.
171    fn is_active(&self) -> bool;
172}
173
174// ============================================================================
175// SRTP Cipher Factory Traits
176// ============================================================================
177
178/// Factory for AES-128-CM-SHA1-80 cipher instances.
179pub trait SupportedAes128CmSha1_80: CryptoSafe {
180    /// Create a cipher instance with the given key.
181    fn create_cipher(&self, key: [u8; 16], encrypt: bool) -> Box<dyn Aes128CmSha1_80Cipher>;
182}
183
184/// Factory for AEAD-AES-128-GCM cipher instances.
185pub trait SupportedAeadAes128Gcm: CryptoSafe {
186    /// Create a cipher instance with the given key.
187    fn create_cipher(&self, key: [u8; 16], encrypt: bool) -> Box<dyn AeadAes128GcmCipher>;
188}
189
190/// Factory for AEAD-AES-256-GCM cipher instances.
191pub trait SupportedAeadAes256Gcm: CryptoSafe {
192    /// Create a cipher instance with the given key.
193    fn create_cipher(&self, key: [u8; 32], encrypt: bool) -> Box<dyn AeadAes256GcmCipher>;
194}
195
196// ============================================================================
197// SRTP Cipher Instance Traits
198// ============================================================================
199
200/// AES-128-CM-SHA1-80 cipher instance for SRTP encryption/decryption.
201pub trait Aes128CmSha1_80Cipher: CryptoSafe {
202    /// Encrypt input with the given IV.
203    fn encrypt(
204        &mut self,
205        iv: &[u8; 16],
206        input: &[u8],
207        output: &mut [u8],
208    ) -> Result<(), CryptoError>;
209
210    /// Decrypt input with the given IV.
211    fn decrypt(
212        &mut self,
213        iv: &[u8; 16],
214        input: &[u8],
215        output: &mut [u8],
216    ) -> Result<(), CryptoError>;
217}
218
219/// AEAD-AES-128-GCM cipher instance for SRTP encryption/decryption.
220pub trait AeadAes128GcmCipher: CryptoSafe {
221    /// Encrypt input with the given IV and AAD.
222    fn encrypt(
223        &mut self,
224        iv: &[u8; 12],
225        aad: &[u8],
226        input: &[u8],
227        output: &mut [u8],
228    ) -> Result<(), CryptoError>;
229
230    /// Decrypt input with the given IV and AADs.
231    fn decrypt(
232        &mut self,
233        iv: &[u8; 12],
234        aads: &[&[u8]],
235        input: &[u8],
236        output: &mut [u8],
237    ) -> Result<usize, CryptoError>;
238}
239
240/// AEAD-AES-256-GCM cipher instance for SRTP encryption/decryption.
241pub trait AeadAes256GcmCipher: CryptoSafe {
242    /// Encrypt input with the given IV and AAD.
243    fn encrypt(
244        &mut self,
245        iv: &[u8; 12],
246        aad: &[u8],
247        input: &[u8],
248        output: &mut [u8],
249    ) -> Result<(), CryptoError>;
250
251    /// Decrypt input with the given IV and AADs.
252    fn decrypt(
253        &mut self,
254        iv: &[u8; 12],
255        aads: &[&[u8]],
256        input: &[u8],
257        output: &mut [u8],
258    ) -> Result<usize, CryptoError>;
259}
260
261// ============================================================================
262// SRTP Profile Definitions
263// ============================================================================
264
265/// AES-128-CM-SHA1-80 SRTP profile.
266///
267/// This struct provides constants, types, and protocol functions for the
268/// AES-128-CM-SHA1-80 SRTP protection profile (RFC 5764).
269#[derive(Debug, Clone, Copy)]
270pub struct Aes128CmSha1_80;
271
272impl Aes128CmSha1_80 {
273    /// Key length in bytes.
274    pub const KEY_LEN: usize = 16;
275    /// Salt length in bytes.
276    pub const SALT_LEN: usize = 14;
277    /// HMAC key length in bytes.
278    pub const HMAC_KEY_LEN: usize = 20;
279    /// HMAC tag length in bytes.
280    pub const HMAC_TAG_LEN: usize = 10;
281
282    /// Compute and append RTP HMAC tag.
283    pub fn rtp_hmac(
284        sha1_hmac: impl Fn(&[u8], &[&[u8]]) -> [u8; 20],
285        key: &[u8],
286        buf: &mut [u8],
287        srtp_index: u64,
288        hmac_start: usize,
289    ) {
290        let roc = (srtp_index >> 16) as u32;
291        let tag = sha1_hmac(key, &[&buf[..hmac_start], &roc.to_be_bytes()]);
292        buf[hmac_start..(hmac_start + Self::HMAC_TAG_LEN)]
293            .copy_from_slice(&tag[0..Self::HMAC_TAG_LEN]);
294    }
295
296    /// Verify RTP HMAC tag.
297    pub fn rtp_verify(
298        sha1_hmac: impl Fn(&[u8], &[&[u8]]) -> [u8; 20],
299        key: &[u8],
300        buf: &[u8],
301        srtp_index: u64,
302        cmp: &[u8],
303    ) -> bool {
304        let roc = (srtp_index >> 16) as u32;
305        let tag = sha1_hmac(key, &[buf, &roc.to_be_bytes()]);
306        tag[0..Self::HMAC_TAG_LEN].ct_eq(cmp).into()
307    }
308
309    /// Compute RTP IV.
310    pub fn rtp_iv(salt: [u8; 14], ssrc: u32, srtp_index: u64) -> [u8; 16] {
311        let mut iv = [0; 16];
312        let ssrc_be = ssrc.to_be_bytes();
313        let srtp_be = srtp_index.to_be_bytes();
314        iv[4..8].copy_from_slice(&ssrc_be);
315        for i in 0..8 {
316            iv[i + 6] ^= srtp_be[i];
317        }
318        for i in 0..14 {
319            iv[i] ^= salt[i];
320        }
321        iv
322    }
323
324    /// Compute and append RTCP HMAC tag.
325    pub fn rtcp_hmac(
326        sha1_hmac: impl Fn(&[u8], &[&[u8]]) -> [u8; 20],
327        key: &[u8],
328        buf: &mut [u8],
329        hmac_index: usize,
330    ) {
331        let tag = sha1_hmac(key, &[&buf[0..hmac_index]]);
332        buf[hmac_index..(hmac_index + Self::HMAC_TAG_LEN)]
333            .copy_from_slice(&tag[0..Self::HMAC_TAG_LEN]);
334    }
335
336    /// Verify RTCP HMAC tag.
337    pub fn rtcp_verify(
338        sha1_hmac: impl Fn(&[u8], &[&[u8]]) -> [u8; 20],
339        key: &[u8],
340        buf: &[u8],
341        cmp: &[u8],
342    ) -> bool {
343        let tag = sha1_hmac(key, &[buf]);
344        tag[0..Self::HMAC_TAG_LEN].ct_eq(cmp).into()
345    }
346}
347
348/// AEAD-AES-128-GCM SRTP profile.
349///
350/// This struct provides constants, types, and protocol functions for the
351/// AEAD-AES-128-GCM SRTP protection profile (RFC 7714).
352#[derive(Debug, Clone, Copy)]
353pub struct AeadAes128Gcm;
354
355impl AeadAes128Gcm {
356    /// Key length in bytes.
357    pub const KEY_LEN: usize = 16;
358    /// Salt length in bytes.
359    pub const SALT_LEN: usize = 12;
360    /// RTCP AAD length in bytes.
361    pub const RTCP_AAD_LEN: usize = 12;
362    /// Authentication tag length in bytes.
363    pub const TAG_LEN: usize = 16;
364    /// IV length in bytes.
365    pub const IV_LEN: usize = 12;
366
367    /// Compute RTP IV.
368    pub fn rtp_iv(salt: [u8; 12], ssrc: u32, roc: u32, seq: u16) -> [u8; 12] {
369        // See: https://www.rfc-editor.org/rfc/rfc7714#section-8.1
370        let mut iv = [0; 12];
371
372        let ssrc_be = ssrc.to_be_bytes();
373        let roc_be = roc.to_be_bytes();
374        let seq_be = seq.to_be_bytes();
375
376        iv[2..6].copy_from_slice(&ssrc_be);
377        iv[6..10].copy_from_slice(&roc_be);
378        iv[10..12].copy_from_slice(&seq_be);
379
380        for i in 0..12 {
381            iv[i] ^= salt[i];
382        }
383
384        iv
385    }
386
387    /// Compute RTCP IV.
388    pub fn rtcp_iv(salt: [u8; 12], ssrc: u32, srtp_index: u32) -> [u8; 12] {
389        // See: https://www.rfc-editor.org/rfc/rfc7714#section-9.1
390        let mut iv = [0; 12];
391
392        let ssrc_be = ssrc.to_be_bytes();
393        let srtp_be = srtp_index.to_be_bytes();
394
395        iv[2..6].copy_from_slice(&ssrc_be);
396        iv[8..12].copy_from_slice(&srtp_be);
397
398        for i in 0..12 {
399            iv[i] ^= salt[i];
400        }
401
402        iv
403    }
404}
405
406/// AEAD-AES-256-GCM SRTP profile.
407///
408/// This struct provides constants, types, and protocol functions for the
409/// AEAD-AES-256-GCM SRTP protection profile (RFC 7714).
410#[derive(Debug, Clone, Copy)]
411pub struct AeadAes256Gcm;
412
413impl AeadAes256Gcm {
414    /// Key length in bytes.
415    pub const KEY_LEN: usize = 32;
416    /// Salt length in bytes.
417    pub const SALT_LEN: usize = 12;
418    /// RTCP AAD length in bytes.
419    pub const RTCP_AAD_LEN: usize = 12;
420    /// Authentication tag length in bytes.
421    pub const TAG_LEN: usize = 16;
422    /// IV length in bytes.
423    pub const IV_LEN: usize = 12;
424
425    /// Compute RTP IV.
426    pub fn rtp_iv(salt: [u8; 12], ssrc: u32, roc: u32, seq: u16) -> [u8; 12] {
427        // See: https://www.rfc-editor.org/rfc/rfc7714#section-8.1
428        let mut iv = [0; 12];
429
430        let ssrc_be = ssrc.to_be_bytes();
431        let roc_be = roc.to_be_bytes();
432        let seq_be = seq.to_be_bytes();
433
434        iv[2..6].copy_from_slice(&ssrc_be);
435        iv[6..10].copy_from_slice(&roc_be);
436        iv[10..12].copy_from_slice(&seq_be);
437
438        for i in 0..12 {
439            iv[i] ^= salt[i];
440        }
441
442        iv
443    }
444
445    /// Compute RTCP IV.
446    pub fn rtcp_iv(salt: [u8; 12], ssrc: u32, srtp_index: u32) -> [u8; 12] {
447        // See: https://www.rfc-editor.org/rfc/rfc7714#section-9.1
448        let mut iv = [0; 12];
449
450        let ssrc_be = ssrc.to_be_bytes();
451        let srtp_be = srtp_index.to_be_bytes();
452
453        iv[2..6].copy_from_slice(&ssrc_be);
454        iv[8..12].copy_from_slice(&srtp_be);
455
456        for i in 0..12 {
457            iv[i] ^= salt[i];
458        }
459
460        iv
461    }
462}