use std::fmt; use self::aes_128_cm_sha1_80::AesKey; use super::CryptoProvider; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SrtpProfile { #[cfg(feature = "_internal_test_exports")] PassThrough, Aes128CmSha1_80, AeadAes128Gcm, AeadAes256Gcm, } #[allow(dead_code)] impl SrtpProfile { // All the profiles we support, ordered from most preferred to least. pub(crate) const ALL: &'static [SrtpProfile] = &[ SrtpProfile::AeadAes256Gcm, SrtpProfile::AeadAes128Gcm, SrtpProfile::Aes128CmSha1_80, ]; /// The length of keying material to extract from the DTLS session in bytes. #[rustfmt::skip] pub(crate) fn keying_material_len(&self) -> usize { match self { #[cfg(feature = "_internal_test_exports")] SrtpProfile::PassThrough => 0, // MASTER_KEY_LEN * 2 + MASTER_SALT * 2 // TODO: This is a duplication of info that is held in srtp.rs, because we // don't want a dependency in that direction. SrtpProfile::Aes128CmSha1_80 => 16 * 2 + 14 * 2, SrtpProfile::AeadAes128Gcm => 16 * 2 + 12 * 2, SrtpProfile::AeadAes256Gcm => 32 * 2 + 12 * 2, } } } pub enum SrtpCrypto { #[cfg(any(feature = "openssl", feature = "openssl-dimpl"))] OpenSsl(super::ossl::OsslSrtpCryptoImpl), #[cfg(not(any(feature = "openssl", feature = "openssl-dimpl")))] OpenSsl(DummySrtpCryptoImpl), #[cfg(all(any(feature = "wincrypto", feature = "wincrypto-dimpl"), target_os = "windows"))] WinCrypto(super::wincrypto::WinCryptoSrtpCryptoImpl), #[cfg(not(all(any(feature = "wincrypto", feature = "wincrypto-dimpl"), target_os = "windows")))] WinCrypto(DummySrtpCryptoImpl), #[cfg(feature = "dimpl")] AwsLc(super::aws_lc::AwsLcImpl), #[cfg(not(feature = "dimpl"))] AwsLc(DummySrtpCryptoImpl), } #[allow(clippy::unit_arg)] impl SrtpCrypto { #[cfg(any(feature = "openssl", feature = "openssl-dimpl"))] pub fn new_openssl() -> SrtpCrypto { Self::OpenSsl(super::ossl::OsslSrtpCryptoImpl) } #[cfg(not(any(feature = "openssl", feature = "openssl-dimpl")))] pub fn new_openssl() -> SrtpCrypto { Self::OpenSsl(DummySrtpCryptoImpl(CryptoProvider::OpenSsl)) } #[cfg(all(any(feature = "wincrypto", feature = "wincrypto-dimpl"), target_os = "windows"))] pub fn new_wincrypto() -> SrtpCrypto { Self::WinCrypto(super::wincrypto::WinCryptoSrtpCryptoImpl) } #[cfg(not(all(any(feature = "wincrypto", feature = "wincrypto-dimpl"), target_os = "windows")))] pub fn new_wincrypto() -> SrtpCrypto { Self::WinCrypto(DummySrtpCryptoImpl(CryptoProvider::WinCrypto)) } #[cfg(feature = "dimpl")] pub fn new_rust_crypto() -> SrtpCrypto { Self::AwsLc(super::aws_lc::AwsLcImpl) } #[cfg(not(feature = "dimpl"))] pub fn new_rust_crypto() -> SrtpCrypto { Self::AwsLc(DummySrtpCryptoImpl(CryptoProvider::Dimpl)) } // TODO: Can we avoice dynamic dispatch in this signature? The parameters are: // 1. As few "touch points" beteen rtp/srtp.rs and here as possible. // 2. Clear contract towards the actual impl. // 3. Choice of impl passed all the way from RtcConfig. pub fn new_aes_128_cm_sha1_80( &self, key: AesKey, encrypt: bool, ) -> Box { match self { SrtpCrypto::OpenSsl(v) => Box::new(v.new_aes_128_cm_sha1_80(key, encrypt)), SrtpCrypto::WinCrypto(v) => Box::new(v.new_aes_128_cm_sha1_80(key, encrypt)), SrtpCrypto::AwsLc(v) => Box::new(v.new_aes_128_cm_sha1_80(key, encrypt)), } } pub fn new_aead_aes_128_gcm( &self, key: aead_aes_128_gcm::AeadKey, encrypt: bool, ) -> Box { match self { SrtpCrypto::OpenSsl(v) => Box::new(v.new_aead_aes_128_gcm(key, encrypt)), SrtpCrypto::WinCrypto(v) => Box::new(v.new_aead_aes_128_gcm(key, encrypt)), SrtpCrypto::AwsLc(v) => Box::new(v.new_aead_aes_128_gcm(key, encrypt)), } } pub fn new_aead_aes_256_gcm( &self, key: aead_aes_256_gcm::AeadKey, encrypt: bool, ) -> Box { match self { SrtpCrypto::OpenSsl(v) => Box::new(v.new_aead_aes_256_gcm(key, encrypt)), SrtpCrypto::WinCrypto(v) => Box::new(v.new_aead_aes_256_gcm(key, encrypt)), SrtpCrypto::AwsLc(v) => Box::new(v.new_aead_aes_256_gcm(key, encrypt)), } } pub fn srtp_aes_128_ecb_round(&self, key: &[u8], input: &[u8], output: &mut [u8]) { match self { SrtpCrypto::OpenSsl(v) => v.srtp_aes_128_ecb_round(key, input, output), SrtpCrypto::WinCrypto(v) => v.srtp_aes_128_ecb_round(key, input, output), SrtpCrypto::AwsLc(v) => v.srtp_aes_128_ecb_round(key, input, output), } } pub fn srtp_aes_256_ecb_round(&self, key: &[u8], input: &[u8], output: &mut [u8]) { match self { SrtpCrypto::OpenSsl(v) => v.srtp_aes_256_ecb_round(key, input, output), SrtpCrypto::WinCrypto(v) => v.srtp_aes_256_ecb_round(key, input, output), SrtpCrypto::AwsLc(v) => v.srtp_aes_256_ecb_round(key, input, output), } } } pub trait SrtpCryptoImpl { type Aes128CmSha1_80: aes_128_cm_sha1_80::CipherCtx; type AeadAes128Gcm: aead_aes_128_gcm::CipherCtx; type AeadAes256Gcm: aead_aes_256_gcm::CipherCtx; fn new_aes_128_cm_sha1_80(&self, key: AesKey, encrypt: bool) -> Self::Aes128CmSha1_80 { ::new(key, encrypt) } fn new_aead_aes_128_gcm( &self, key: aead_aes_128_gcm::AeadKey, encrypt: bool, ) -> Self::AeadAes128Gcm { ::new(key, encrypt) } fn new_aead_aes_256_gcm( &self, key: aead_aes_256_gcm::AeadKey, encrypt: bool, ) -> Self::AeadAes256Gcm { ::new(key, encrypt) } fn srtp_aes_128_ecb_round(&self, key: &[u8], input: &[u8], output: &mut [u8]); fn srtp_aes_256_ecb_round(&self, key: &[u8], input: &[u8], output: &mut [u8]); } pub mod aes_128_cm_sha1_80 { use std::panic::UnwindSafe; use subtle::ConstantTimeEq; use crate::crypto::CryptoError; pub const KEY_LEN: usize = 16; pub const SALT_LEN: usize = 14; pub const HMAC_KEY_LEN: usize = 20; pub const HMAC_TAG_LEN: usize = 10; pub type AesKey = [u8; 16]; pub type RtpSalt = [u8; 14]; pub type RtpIv = [u8; 16]; pub trait CipherCtx: UnwindSafe + Send + Sync { fn new(key: AesKey, encrypt: bool) -> Self where Self: Sized; fn encrypt( &mut self, iv: &RtpIv, input: &[u8], output: &mut [u8], ) -> Result<(), CryptoError>; fn decrypt( &mut self, iv: &RtpIv, input: &[u8], output: &mut [u8], ) -> Result<(), CryptoError>; } pub fn rtp_hmac(key: &[u8], buf: &mut [u8], srtp_index: u64, hmac_start: usize) { let roc = (srtp_index >> 16) as u32; let tag = crate::crypto::sha1_hmac(key, &[&buf[..hmac_start], &roc.to_be_bytes()]); buf[hmac_start..(hmac_start + HMAC_TAG_LEN)].copy_from_slice(&tag[0..HMAC_TAG_LEN]); } pub fn rtp_verify(key: &[u8], buf: &[u8], srtp_index: u64, cmp: &[u8]) -> bool { let roc = (srtp_index >> 16) as u32; let tag = crate::crypto::sha1_hmac(key, &[buf, &roc.to_be_bytes()]); tag[0..HMAC_TAG_LEN].ct_eq(cmp).into() } pub fn rtp_iv(salt: RtpSalt, ssrc: u32, srtp_index: u64) -> RtpIv { let mut iv = [0; 16]; let ssrc_be = ssrc.to_be_bytes(); let srtp_be = srtp_index.to_be_bytes(); iv[4..8].copy_from_slice(&ssrc_be); for i in 0..8 { iv[i + 6] ^= srtp_be[i]; } for i in 0..14 { iv[i] ^= salt[i]; } iv } pub fn rtcp_hmac(key: &[u8], buf: &mut [u8], hmac_index: usize) { let tag = crate::crypto::sha1_hmac(key, &[&buf[0..hmac_index]]); buf[hmac_index..(hmac_index + HMAC_TAG_LEN)].copy_from_slice(&tag[0..HMAC_TAG_LEN]); } pub fn rtcp_verify(key: &[u8], buf: &[u8], cmp: &[u8]) -> bool { let tag = crate::crypto::sha1_hmac(key, &[buf]); tag[0..HMAC_TAG_LEN].ct_eq(cmp).into() } } pub mod aead_aes_128_gcm { use std::panic::UnwindSafe; use crate::crypto::CryptoError; pub const KEY_LEN: usize = 16; pub const SALT_LEN: usize = 12; pub const RTCP_AAD_LEN: usize = 12; pub const TAG_LEN: usize = 16; pub const IV_LEN: usize = 12; pub type AeadKey = [u8; KEY_LEN]; pub type RtpSalt = [u8; SALT_LEN]; pub type RtpIv = [u8; SALT_LEN]; pub trait CipherCtx: UnwindSafe + Send + Sync { fn new(key: AeadKey, encrypt: bool) -> Self where Self: Sized; fn encrypt( &mut self, iv: &[u8; IV_LEN], aad: &[u8], input: &[u8], output: &mut [u8], ) -> Result<(), CryptoError>; fn decrypt( &mut self, iv: &[u8; IV_LEN], aads: &[&[u8]], input: &[u8], output: &mut [u8], ) -> Result; } pub fn rtp_iv(salt: RtpSalt, ssrc: u32, roc: u32, seq: u16) -> RtpIv { // See: https://www.rfc-editor.org/rfc/rfc7714#section-8.1 // TODO: See if this is faster if rewritten for u128 let mut iv = [0; SALT_LEN]; let ssrc_be = ssrc.to_be_bytes(); let roc_be = roc.to_be_bytes(); let seq_be = seq.to_be_bytes(); iv[2..6].copy_from_slice(&ssrc_be); iv[6..10].copy_from_slice(&roc_be); iv[10..12].copy_from_slice(&seq_be); // XOR with salt for i in 0..SALT_LEN { iv[i] ^= salt[i]; } iv } pub fn rtcp_iv(salt: RtpSalt, ssrc: u32, srtp_index: u32) -> RtpIv { // See: https://www.rfc-editor.org/rfc/rfc7714#section-9.1 // TODO: See if this is faster if rewritten for u128 let mut iv = [0; SALT_LEN]; let ssrc_be = ssrc.to_be_bytes(); let srtp_be = srtp_index.to_be_bytes(); iv[2..6].copy_from_slice(&ssrc_be); iv[8..12].copy_from_slice(&srtp_be); // XOR with salt for i in 0..SALT_LEN { iv[i] ^= salt[i]; } iv } } pub mod aead_aes_256_gcm { use std::panic::UnwindSafe; use crate::crypto::CryptoError; pub const KEY_LEN: usize = 32; pub const SALT_LEN: usize = 12; pub const RTCP_AAD_LEN: usize = 12; pub const TAG_LEN: usize = 16; pub const IV_LEN: usize = 12; pub type AeadKey = [u8; KEY_LEN]; pub type RtpSalt = [u8; SALT_LEN]; pub type RtpIv = [u8; SALT_LEN]; pub trait CipherCtx: UnwindSafe + Send + Sync { fn new(key: AeadKey, encrypt: bool) -> Self where Self: Sized; fn encrypt( &mut self, iv: &[u8; IV_LEN], aad: &[u8], input: &[u8], output: &mut [u8], ) -> Result<(), CryptoError>; fn decrypt( &mut self, iv: &[u8; IV_LEN], aads: &[&[u8]], input: &[u8], output: &mut [u8], ) -> Result; } pub fn rtp_iv(salt: RtpSalt, ssrc: u32, roc: u32, seq: u16) -> RtpIv { // See: https://www.rfc-editor.org/rfc/rfc7714#section-8.1 // TODO: See if this is faster if rewritten for u128 let mut iv = [0; SALT_LEN]; let ssrc_be = ssrc.to_be_bytes(); let roc_be = roc.to_be_bytes(); let seq_be = seq.to_be_bytes(); iv[2..6].copy_from_slice(&ssrc_be); iv[6..10].copy_from_slice(&roc_be); iv[10..12].copy_from_slice(&seq_be); // XOR with salt for i in 0..SALT_LEN { iv[i] ^= salt[i]; } iv } pub fn rtcp_iv(salt: RtpSalt, ssrc: u32, srtp_index: u32) -> RtpIv { // See: https://www.rfc-editor.org/rfc/rfc7714#section-9.1 // TODO: See if this is faster if rewritten for u128 let mut iv = [0; SALT_LEN]; let ssrc_be = ssrc.to_be_bytes(); let srtp_be = srtp_index.to_be_bytes(); iv[2..6].copy_from_slice(&ssrc_be); iv[8..12].copy_from_slice(&srtp_be); // XOR with salt for i in 0..SALT_LEN { iv[i] ^= salt[i]; } iv } } impl fmt::Display for SrtpProfile { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { #[cfg(feature = "_internal_test_exports")] SrtpProfile::PassThrough => write!(f, "PassThrough"), SrtpProfile::Aes128CmSha1_80 => write!(f, "SRTP_AES128_CM_SHA1_80"), SrtpProfile::AeadAes128Gcm => write!(f, "SRTP_AEAD_AES_128_GCM"), SrtpProfile::AeadAes256Gcm => write!(f, "SRTP_AEAD_AES_256_GCM"), } } } pub struct DummySrtpCryptoImpl(CryptoProvider); impl SrtpCryptoImpl for DummySrtpCryptoImpl { type Aes128CmSha1_80 = (); type AeadAes128Gcm = (); type AeadAes256Gcm = (); fn new_aes_128_cm_sha1_80(&self, _: AesKey, _: bool) -> Self::Aes128CmSha1_80 { panic!("Must enable feature: {}", self.0) } fn new_aead_aes_128_gcm(&self, _: aead_aes_128_gcm::AeadKey, _: bool) -> Self::AeadAes128Gcm { panic!("Must enable feature: {}", self.0) } fn new_aead_aes_256_gcm(&self, _: aead_aes_256_gcm::AeadKey, _: bool) -> Self::AeadAes256Gcm { panic!("Must enable feature: {}", self.0) } fn srtp_aes_128_ecb_round(&self, _: &[u8], _: &[u8], _: &mut [u8]) { panic!("Must enable feature: {}", self.0) } fn srtp_aes_256_ecb_round(&self, _: &[u8], _: &[u8], _: &mut [u8]) { panic!("Must enable feature: {}", self.0) } } impl aes_128_cm_sha1_80::CipherCtx for () { fn new(_: AesKey, _: bool) -> Self where Self: Sized, { unreachable!() } fn encrypt( &mut self, _: &aes_128_cm_sha1_80::RtpIv, _: &[u8], _: &mut [u8], ) -> Result<(), super::CryptoError> { unreachable!() } fn decrypt( &mut self, _: &aes_128_cm_sha1_80::RtpIv, _: &[u8], _: &mut [u8], ) -> Result<(), super::CryptoError> { unreachable!() } } impl aead_aes_128_gcm::CipherCtx for () { fn new(_: aead_aes_128_gcm::AeadKey, _: bool) -> Self where Self: Sized, { unreachable!() } fn encrypt( &mut self, _: &[u8; aead_aes_128_gcm::IV_LEN], _: &[u8], _: &[u8], _: &mut [u8], ) -> Result<(), super::CryptoError> { unreachable!() } fn decrypt( &mut self, _: &[u8; aead_aes_128_gcm::IV_LEN], _: &[&[u8]], _: &[u8], _: &mut [u8], ) -> Result { unreachable!() } } impl aead_aes_256_gcm::CipherCtx for () { fn new(_: aead_aes_256_gcm::AeadKey, _: bool) -> Self where Self: Sized, { unreachable!() } fn encrypt( &mut self, _: &[u8; aead_aes_256_gcm::IV_LEN], _: &[u8], _: &[u8], _: &mut [u8], ) -> Result<(), super::CryptoError> { unreachable!() } fn decrypt( &mut self, _: &[u8; aead_aes_256_gcm::IV_LEN], _: &[&[u8]], _: &[u8], _: &mut [u8], ) -> Result { unreachable!() } }