use byteorder::{BigEndian, ByteOrder};
use bytes::{BufMut, BytesMut};
use crypto::{
HmacAlgorithm, Mac, RTCCrypto, SecretVec, StreamCipher, StreamCipherAlgorithm, constant_time_eq,
};
use rtcp::header::{HEADER_LENGTH, SSRC_LENGTH};
use shared::marshal::*;
use super::{Cipher, Kdf, crypto_error};
use crate::key_derivation::*;
use crate::protection_profile::*;
use shared::error::{Error, Result};
pub const CIPHER_AES_CM_HMAC_SHA1AUTH_TAG_LEN: usize = 10;
pub(crate) struct CipherAesCmHmacSha1 {
profile: ProtectionProfile,
srtp_session_salt: Vec<u8>,
srtp_session_auth: Box<dyn Mac>,
srtcp_session_salt: Vec<u8>,
srtcp_session_auth: Box<dyn Mac>,
srtp_cipher: Box<dyn StreamCipher>,
srtcp_cipher: Box<dyn StreamCipher>,
}
impl CipherAesCmHmacSha1 {
pub fn new(
profile: ProtectionProfile,
master_key: &[u8],
master_salt: &[u8],
crypto: &dyn RTCCrypto,
) -> Result<Self> {
let (kdf, algorithm): (Kdf, StreamCipherAlgorithm) = match profile {
ProtectionProfile::Aes128CmHmacSha1_32 | ProtectionProfile::Aes128CmHmacSha1_80 => {
(aes_cm_key_derivation, StreamCipherAlgorithm::Aes128Ctr)
}
ProtectionProfile::Aes256CmHmacSha1_80 | ProtectionProfile::Aes256CmHmacSha1_32 => {
(aes_256_cm_key_derivation, StreamCipherAlgorithm::Aes256Ctr)
}
_ => {
return Err(Error::Other(String::from(
"no AES protection profile passed to CipherAesCmHmacSha1",
)));
}
};
let srtp_session_key = SecretVec::new(kdf(
crypto,
LABEL_SRTP_ENCRYPTION,
master_key,
master_salt,
0,
master_key.len(),
)?);
let srtcp_session_key = SecretVec::new(kdf(
crypto,
LABEL_SRTCP_ENCRYPTION,
master_key,
master_salt,
0,
master_key.len(),
)?);
let srtp_cipher = crypto
.new_stream_cipher(algorithm, srtp_session_key.as_ref())
.map_err(crypto_error)?;
let srtcp_cipher = crypto
.new_stream_cipher(algorithm, srtcp_session_key.as_ref())
.map_err(crypto_error)?;
let srtp_session_salt = kdf(
crypto,
LABEL_SRTP_SALT,
master_key,
master_salt,
0,
master_salt.len(),
)?;
let srtcp_session_salt = kdf(
crypto,
LABEL_SRTCP_SALT,
master_key,
master_salt,
0,
master_salt.len(),
)?;
let auth_key_len = profile.auth_key_len();
let srtp_session_auth = SecretVec::new(kdf(
crypto,
LABEL_SRTP_AUTHENTICATION_TAG,
master_key,
master_salt,
0,
auth_key_len,
)?);
let srtcp_session_auth = SecretVec::new(kdf(
crypto,
LABEL_SRTCP_AUTHENTICATION_TAG,
master_key,
master_salt,
0,
auth_key_len,
)?);
let srtp_session_auth = crypto
.new_hmac(HmacAlgorithm::Sha1, srtp_session_auth.as_ref())
.map_err(crypto_error)?;
let srtcp_session_auth = crypto
.new_hmac(HmacAlgorithm::Sha1, srtcp_session_auth.as_ref())
.map_err(crypto_error)?;
Ok(Self {
profile,
srtp_session_salt,
srtp_session_auth,
srtcp_session_salt,
srtcp_session_auth,
srtp_cipher,
srtcp_cipher,
})
}
fn generate_srtp_auth_tag(&mut self, buf: &[u8], roc: u32) -> Result<[u8; 20]> {
let mut tag = [0; 20];
self.srtp_session_auth
.sign(&[buf, &roc.to_be_bytes()], &mut tag)
.map_err(crypto_error)?;
Ok(tag)
}
fn generate_srtcp_auth_tag(&mut self, buf: &[u8]) -> Result<[u8; 20]> {
let mut tag = [0; 20];
self.srtcp_session_auth
.sign(&[buf], &mut tag)
.map_err(crypto_error)?;
Ok(tag)
}
}
impl Cipher for CipherAesCmHmacSha1 {
fn rtp_auth_tag_len(&self) -> usize {
self.profile.rtp_auth_tag_len()
}
fn rtcp_auth_tag_len(&self) -> usize {
self.profile.rtcp_auth_tag_len()
}
fn aead_auth_tag_len(&self) -> usize {
self.profile.aead_auth_tag_len()
}
fn get_rtcp_index(&self, input: &[u8]) -> usize {
let tail_offset = input.len() - (self.profile.rtcp_auth_tag_len() + SRTCP_INDEX_SIZE);
(BigEndian::read_u32(&input[tail_offset..tail_offset + SRTCP_INDEX_SIZE]) & !(1 << 31))
as usize
}
fn encrypt_rtp(
&mut self,
plaintext: &[u8],
header: &rtp::Header,
roc: u32,
) -> Result<BytesMut> {
let mut writer = BytesMut::with_capacity(plaintext.len() + self.rtp_auth_tag_len());
writer.extend_from_slice(plaintext);
let counter = generate_counter(
header.sequence_number,
roc,
header.ssrc,
&self.srtp_session_salt,
);
self.srtp_cipher
.apply_keystream(&counter, &mut writer[header.marshal_size()..])
.map_err(crypto_error)?;
let full_auth_tag = self.generate_srtp_auth_tag(&writer, roc)?;
let auth_tag = &full_auth_tag[..self.rtp_auth_tag_len()];
writer.extend_from_slice(auth_tag);
Ok(writer)
}
fn decrypt_rtp(
&mut self,
encrypted: &[u8],
header: &rtp::Header,
roc: u32,
) -> Result<BytesMut> {
let encrypted_len = encrypted.len();
if encrypted_len < self.rtp_auth_tag_len() {
return Err(Error::SrtpTooSmall(encrypted_len, self.rtp_auth_tag_len()));
}
let mut writer = BytesMut::with_capacity(encrypted_len - self.rtp_auth_tag_len());
let actual_tag = &encrypted[encrypted_len - self.rtp_auth_tag_len()..];
let cipher_text = &encrypted[..encrypted_len - self.rtp_auth_tag_len()];
let full_expected_tag = self.generate_srtp_auth_tag(cipher_text, roc)?;
let expected_tag = &full_expected_tag[..self.rtp_auth_tag_len()];
if !constant_time_eq(actual_tag, expected_tag) {
return Err(Error::RtpFailedToVerifyAuthTag);
}
writer.extend_from_slice(cipher_text);
let counter = generate_counter(
header.sequence_number,
roc,
header.ssrc,
&self.srtp_session_salt,
);
self.srtp_cipher
.apply_keystream(&counter, &mut writer[header.marshal_size()..])
.map_err(crypto_error)?;
Ok(writer)
}
fn encrypt_rtcp(
&mut self,
decrypted: &[u8],
srtcp_index: usize,
ssrc: u32,
) -> Result<BytesMut> {
let mut writer =
BytesMut::with_capacity(decrypted.len() + SRTCP_INDEX_SIZE + self.rtcp_auth_tag_len());
writer.extend_from_slice(decrypted);
let counter = generate_counter(
(srtcp_index & 0xFFFF) as u16,
(srtcp_index >> 16) as u32,
ssrc,
&self.srtcp_session_salt,
);
self.srtcp_cipher
.apply_keystream(&counter, &mut writer[HEADER_LENGTH + SSRC_LENGTH..])
.map_err(crypto_error)?;
writer.put_u32(srtcp_index as u32 | (1u32 << 31));
let full_auth_tag = self.generate_srtcp_auth_tag(&writer)?;
let auth_tag = &full_auth_tag[..self.rtcp_auth_tag_len()];
writer.extend_from_slice(auth_tag);
Ok(writer)
}
fn decrypt_rtcp(
&mut self,
encrypted: &[u8],
srtcp_index: usize,
ssrc: u32,
) -> Result<BytesMut> {
let encrypted_len = encrypted.len();
if encrypted_len < self.rtcp_auth_tag_len() + SRTCP_INDEX_SIZE {
return Err(Error::SrtcpTooSmall(
encrypted_len,
self.rtcp_auth_tag_len() + SRTCP_INDEX_SIZE,
));
}
let tail_offset = encrypted_len - (self.rtcp_auth_tag_len() + SRTCP_INDEX_SIZE);
if tail_offset < 8 {
return Err(Error::ErrTooShortRtcp);
}
let mut writer = BytesMut::with_capacity(tail_offset);
writer.extend_from_slice(&encrypted[0..tail_offset]);
let is_encrypted = encrypted[tail_offset] >> 7;
if is_encrypted == 0 {
return Ok(writer);
}
let actual_tag = &encrypted[encrypted_len - self.rtcp_auth_tag_len()..];
if actual_tag.len() != self.rtcp_auth_tag_len() {
return Err(Error::RtcpInvalidLengthAuthTag(
actual_tag.len(),
self.rtcp_auth_tag_len(),
));
}
let cipher_text = &encrypted[..encrypted_len - self.rtcp_auth_tag_len()];
let full_expected_tag = self.generate_srtcp_auth_tag(cipher_text)?;
let expected_tag = &full_expected_tag[..self.rtcp_auth_tag_len()];
if !constant_time_eq(actual_tag, expected_tag) {
return Err(Error::RtcpFailedToVerifyAuthTag);
}
let counter = generate_counter(
(srtcp_index & 0xFFFF) as u16,
(srtcp_index >> 16) as u32,
ssrc,
&self.srtcp_session_salt,
);
self.srtcp_cipher
.apply_keystream(&counter, &mut writer[HEADER_LENGTH + SSRC_LENGTH..])
.map_err(crypto_error)?;
Ok(writer)
}
}