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}