diff --git a/src/rfc3550/mod.rs b/src/rfc3550/mod.rs index c137ee7..331242f 100644 --- a/src/rfc3550/mod.rs +++ b/src/rfc3550/mod.rs @@ -2,7 +2,7 @@ pub use self::rtp::{RtpPacket, RtpFixedHeader, RtpHeaderExtension, RtpPacketRead pub use self::rtcp::{RtcpPacket, RtcpSenderReport, RtcpReceiverReport, RtcpPacketReader}; pub use self::rtcp::{RtcpSourceDescription, RtcpGoodbye, RtcpApplicationDefined}; -pub use self::rtcp::{ReceptionReport, SdesChunk, SdesItem}; +pub use self::rtcp::{RtcpCompoundPacket, ReceptionReport, SdesChunk, SdesItem}; pub use self::rtcp::{RTCP_PACKET_TYPE_SR, RTCP_PACKET_TYPE_RR, RTCP_PACKET_TYPE_SDES}; pub use self::rtcp::{RTCP_PACKET_TYPE_BYE, RTCP_PACKET_TYPE_APP}; diff --git a/src/rfc3550/rtcp.rs b/src/rfc3550/rtcp.rs index a47c769..29cb632 100644 --- a/src/rfc3550/rtcp.rs +++ b/src/rfc3550/rtcp.rs @@ -26,9 +26,17 @@ pub const SDES_ITEM_TYPE_PRIV: u8 = 8; #[derive(Debug, Clone, PartialEq, Eq)] pub struct RtcpPacketReader; impl traits::ReadPacket for RtcpPacketReader { - type Packet = RtcpPacket; + type Packet = RtcpCompoundPacket; fn read_packet(&mut self, reader: &mut R) -> Result { - RtcpPacket::read_from(reader) + // TODO: optimize + let buf = track_try!(reader.read_all_bytes()); + let mut packets = Vec::new(); + let reader = &mut &buf[..]; + while !reader.is_empty() { + let packet = track_try!(RtcpPacket::read_from(reader)); + packets.push(packet); + } + Ok(RtcpCompoundPacket::new(packets)) } fn supports_type(&self, ty: u8) -> bool { match ty { @@ -45,12 +53,27 @@ impl traits::ReadPacket for RtcpPacketReader { #[derive(Debug, Clone, PartialEq, Eq)] pub struct RtcpPacketWriter; impl traits::WritePacket for RtcpPacketWriter { - type Packet = RtcpPacket; + type Packet = RtcpCompoundPacket; fn write_packet(&mut self, writer: &mut W, packet: &Self::Packet) -> Result<()> { - packet.write_to(writer) + for p in packet.packets.iter() { + track_try!(p.write_to(writer)); + } + Ok(()) } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RtcpCompoundPacket { + pub packets: Vec, +} +impl RtcpCompoundPacket { + pub fn new(packets: Vec) -> Self { + RtcpCompoundPacket { packets: packets } + } +} +impl Packet for RtcpCompoundPacket {} +impl traits::RtcpPacket for RtcpCompoundPacket {} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum RtcpPacket { Sr(RtcpSenderReport), @@ -145,6 +168,7 @@ fn read_sctp(reader: &mut R, expected_type: u8) -> Result<(U5, Vec) let word_count = track_try!(reader.read_u16be()) as usize; let mut payload = track_try!(reader.read_bytes(word_count * 4)); + if padding { let payload_len = payload.len(); track_assert_ne!(payload_len, 0, ErrorKind::Invalid); @@ -431,6 +455,7 @@ impl ReadFrom for SdesChunk { } let len = track_try!(reader.read_u8()) as usize; let text = track_try!(reader.read_string(len)); + read_bytes += 1 + len; let item = match ty { SDES_ITEM_TYPE_CNAME => SdesItem::Cname(text), diff --git a/src/rfc3711.rs b/src/rfc3711.rs index 0ddfcb5..b77fe8a 100644 --- a/src/rfc3711.rs +++ b/src/rfc3711.rs @@ -7,7 +7,7 @@ use handy_async::sync_io::{ReadExt, WriteExt}; use {Result, ErrorKind}; use io::{ReadFrom, WriteTo}; use types::U48; -use traits::ReadPacket; +use traits::{ReadPacket, RtpPacket, RtcpPacket}; use rfc3550; pub type PacketIndex = U48; @@ -91,9 +91,10 @@ impl SrtpContext { let header = track_try!(rfc3550::RtpFixedHeader::read_from(reader)); let encrypted_portion = &reader[0..reader.len() - self.auth_tag_len]; + let index = ((self.rollover_counter as u64) << 32) + (header.seq_num as u64); let iv = BigUint::from_bytes_be(&self.session_salt_key) << 16; let iv = iv ^ (BigUint::from(header.ssrc) << 64); - let iv = iv ^ (BigUint::from(header.seq_num) << 16); + let iv = iv ^ (BigUint::from(index) << 16); let iv = &iv.to_bytes_be()[0..self.session_encr_key.len()]; let mut ctr = @@ -109,10 +110,104 @@ impl SrtpContext { let mut output = [0; 16]; ctr.process(&input[..], &mut output[..]); - let mut temp_block = Vec::from(block); - temp_block.resize(16, 0); - let temp = BigUint::from_bytes_be(&temp_block) ^ BigUint::from_bytes_be(&output); - decrypted.extend(&temp.to_bytes_be()[..block.len()]); + for (a, b) in block.iter().zip(output.iter()) { + decrypted.push(*a ^ *b); + } + } + + Ok(decrypted) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SrtcpContext { + // TODO: support other fields + pub master_key: Vec, + pub master_salt: Vec, + pub highest_recv_index: PacketIndex, // NOTE: 47-bits + pub encryption: EncryptionAlgorithm, + pub replay_list: SplaySet, + pub session_encr_key: Vec, + pub session_salt_key: Vec, + pub session_auth_key: Vec, + pub auth_tag_len: usize, +} +impl SrtcpContext { + pub fn new(master_key: &[u8], master_salt: &[u8]) -> Self { + // TODO: support MKI + SrtcpContext { + master_key: Vec::from(master_key), + master_salt: Vec::from(master_salt), + highest_recv_index: 0, + encryption: EncryptionAlgorithm::default(), + replay_list: SplaySet::new(), + session_encr_key: vec![0; 128 / 8], + session_salt_key: vec![0; 112 / 8], + session_auth_key: vec![0; 160 / 8], + auth_tag_len: 80 / 8, + } + } + pub fn update_session_keys(&mut self) { + // See: https://tools.ietf.org/html/rfc3711#section-4.3.2 + let index = BigUint::from(self.highest_recv_index); + + let enc_key_id = BigUint::from_bytes_be(&[3, 0, 0, 0, 0, 0, 0]) + index.clone(); + let auth_key_id = BigUint::from_bytes_be(&[4, 0, 0, 0, 0, 0, 0]) + index.clone(); + let salt_key_id = BigUint::from_bytes_be(&[5, 0, 0, 0, 0, 0, 0]) + index.clone(); + let master_salt = BigUint::from_bytes_be(&self.master_salt); + + self.session_encr_key = prf_n(&self.master_key, + enc_key_id ^ master_salt.clone(), + self.session_encr_key.len()); + self.session_auth_key = prf_n(&self.master_key, + auth_key_id ^ master_salt.clone(), + self.session_auth_key.len()); + self.session_salt_key = prf_n(&self.master_key, + salt_key_id ^ master_salt.clone(), + self.session_salt_key.len()); + } + pub fn authenticate(&self, packet: &[u8]) -> Result<()> { + let auth_portion = &packet[..packet.len() - self.auth_tag_len]; + let auth_tag = &packet[packet.len() - self.auth_tag_len..]; + + let mut expected_tag = hmac_hash_sha1(&self.session_auth_key, &auth_portion); + expected_tag.truncate(self.auth_tag_len); + track_assert_eq!(auth_tag, &expected_tag[..], ErrorKind::Invalid); + Ok(()) + } + pub fn decrypt(&mut self, packet: &[u8]) -> Result> { + let index = track_try!((&mut &packet[packet.len() - self.auth_tag_len - 4..]).read_u32be()); + let is_encrypted = index & 0x8000_0000 != 0; + if !is_encrypted { + return Ok(Vec::from(&packet[..packet.len() - self.auth_tag_len - 4])); + } + let index = index & 0x7FFF_FFFF; + + let reader = &mut &packet[..]; + let _ = track_try!(reader.read_u32be()); + let ssrc = track_try!(reader.read_u32be()); + let encrypted_portion = &reader[0..reader.len() - self.auth_tag_len - 4]; + + let iv = BigUint::from_bytes_be(&self.session_salt_key) << 16; + let iv = iv ^ (BigUint::from(ssrc) << 64); + let iv = iv ^ (BigUint::from(index) << 16); + let iv = &iv.to_bytes_be()[0..self.session_encr_key.len()]; + + let mut ctr = + crypto::aes::ctr(crypto::aes::KeySize::KeySize128, &self.session_encr_key, iv); + let block_size = self.session_encr_key.len(); + + let mut decrypted = Vec::from(&packet[..8]); + + for (i, block) in encrypted_portion.chunks(block_size).enumerate() { + let mut input = [0; 16]; + (&mut input[8..]).write_u64be(i as u64).unwrap(); + let mut output = [0; 16]; + ctr.process(&input[..], &mut output[..]); + + for (a, b) in block.iter().zip(output.iter()) { + decrypted.push(*a ^ *b); + } } Ok(decrypted) @@ -125,7 +220,8 @@ pub struct SrtpPacketReader { inner: T, } impl SrtpPacketReader - where T: ReadPacket + where T: ReadPacket, + T::Packet: RtpPacket { pub fn new(mut context: SrtpContext, inner: T) -> Self { context.update_session_keys(); @@ -136,7 +232,42 @@ impl SrtpPacketReader } } impl ReadPacket for SrtpPacketReader - where T: ReadPacket + where T: ReadPacket, + T::Packet: RtpPacket +{ + type Packet = T::Packet; + fn read_packet(&mut self, reader: &mut R) -> Result { + let packet_bytes = track_try!(reader.read_all_bytes()); + track_try!(self.context.authenticate(&packet_bytes)); + let decrypted_packet_bytes = track_try!(self.context.decrypt(&packet_bytes)); + track_err!(self.inner.read_packet(&mut &decrypted_packet_bytes[..])) + } + + fn supports_type(&self, ty: u8) -> bool { + self.inner.supports_type(ty) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SrtcpPacketReader { + context: SrtcpContext, + inner: T, +} +impl SrtcpPacketReader + where T: ReadPacket, + T::Packet: RtcpPacket +{ + pub fn new(mut context: SrtcpContext, inner: T) -> Self { + context.update_session_keys(); + SrtcpPacketReader { + context: context, + inner: inner, + } + } +} +impl ReadPacket for SrtcpPacketReader + where T: ReadPacket, + T::Packet: RtcpPacket { type Packet = T::Packet; fn read_packet(&mut self, reader: &mut R) -> Result { @@ -183,6 +314,7 @@ fn prf_n(master_key: &[u8], x: BigUint, n: usize) -> Vec { #[cfg(test)] mod test { use rfc3550; + use rfc4585; use super::*; #[test] @@ -213,4 +345,20 @@ mod test { assert_eq!(&packet.payload[..expected_prefix.len()], &expected_prefix[..]); } + + #[test] + fn rtcp_decryption_works() { + let master_key = [254, 123, 44, 240, 174, 252, 53, 54, 2, 213, 123, 106, 85, 165, 5, 13]; + let master_salt = [77, 202, 202, 112, 81, 101, 219, 232, 143, 131, 160, 89, 15, 141]; + let packet = [128, 201, 0, 1, 194, 242, 138, 93, 67, 38, 193, 233, 60, 78, 188, 195, 230, + 90, 19, 196, 152, 235, 136, 164, 15, 177, 174, 217, 207, 115, 148, 223, 109, + 112, 71, 245, 16, 214, 216, 232, 87, 153, 5, 238, 72, 201, 223, 43, 69, 99, + 54, 211, 118, 28, 227, 100, 161, 216, 90, 203, 99, 167, 215, 130, 151, 16, + 128, 138, 128, 0, 0, 1, 126, 39, 201, 236, 161, 194, 6, 232, 194, 230]; + + let context = SrtcpContext::new(&master_key, &master_salt); + let mut rtcp_reader = SrtcpPacketReader::new(context, rfc4585::RtcpPacketReader); + let packet = track_try_unwrap!(rtcp_reader.read_packet(&mut &packet[..])); + println!("# {:?}", packet); + } } diff --git a/src/rfc4585.rs b/src/rfc4585.rs index 9a523f8..8709ded 100644 --- a/src/rfc4585.rs +++ b/src/rfc4585.rs @@ -21,9 +21,17 @@ pub const PSFB_MESSAGE_TYPE_AFB: u8 = 15; #[derive(Debug, Clone, PartialEq, Eq)] pub struct RtcpPacketReader; impl traits::ReadPacket for RtcpPacketReader { - type Packet = RtcpPacket; + type Packet = rfc3550::RtcpCompoundPacket; fn read_packet(&mut self, reader: &mut R) -> Result { - RtcpPacket::read_from(reader) + // TODO: optimize + let buf = track_try!(reader.read_all_bytes()); + let mut packets = Vec::new(); + let reader = &mut &buf[..]; + while !reader.is_empty() { + let packet = track_try!(RtcpPacket::read_from(reader)); + packets.push(packet); + } + Ok(rfc3550::RtcpCompoundPacket::new(packets)) } fn supports_type(&self, ty: u8) -> bool { match ty { @@ -42,9 +50,12 @@ impl traits::ReadPacket for RtcpPacketReader { #[derive(Debug, Clone, PartialEq, Eq)] pub struct RtcpPacketWriter; impl traits::WritePacket for RtcpPacketWriter { - type Packet = RtcpPacket; + type Packet = rfc3550::RtcpCompoundPacket; fn write_packet(&mut self, writer: &mut W, packet: &Self::Packet) -> Result<()> { - packet.write_to(writer) + for p in packet.packets.iter() { + track_try!(p.write_to(writer)); + } + Ok(()) } }