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}