Add SRT(C)P replay protection
							parent
							
								
									aa4927724b
								
							
						
					
					
						commit
						bda8ce2295
					
				|  | @ -8,7 +8,7 @@ trackable = "0.1" | ||||||
| handy_async = "0.2" | handy_async = "0.2" | ||||||
| rust-crypto = "0.2" | rust-crypto = "0.2" | ||||||
| num = "0.1" | num = "0.1" | ||||||
| splay_tree = "0.2" | fixedbitset = "0.1" | ||||||
| 
 | 
 | ||||||
| [dev-dependencies] | [dev-dependencies] | ||||||
| clap = "2" | clap = "2" | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ extern crate trackable; | ||||||
| extern crate crypto; | extern crate crypto; | ||||||
| extern crate handy_async; | extern crate handy_async; | ||||||
| extern crate num; | extern crate num; | ||||||
| extern crate splay_tree; | extern crate fixedbitset; | ||||||
| 
 | 
 | ||||||
| pub use error::{Error, ErrorKind}; | pub use error::{Error, ErrorKind}; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| use crypto; | use crypto; | ||||||
|  | use fixedbitset::FixedBitSet; | ||||||
| use handy_async::sync_io::{ReadExt, WriteExt}; | use handy_async::sync_io::{ReadExt, WriteExt}; | ||||||
| use num::BigUint; | use num::BigUint; | ||||||
| use splay_tree::SplaySet; |  | ||||||
| use std::borrow::Cow; | use std::borrow::Cow; | ||||||
| use std::io::{Read, Write}; | use std::io::{Read, Write}; | ||||||
| 
 | 
 | ||||||
|  | @ -72,7 +72,8 @@ pub struct Context<P: Protocol> { | ||||||
|     pub key_derivation_rate: u8, |     pub key_derivation_rate: u8, | ||||||
|     pub encryption: EncryptionAlgorithm, |     pub encryption: EncryptionAlgorithm, | ||||||
|     pub authentication: AuthenticationAlgorithm, |     pub authentication: AuthenticationAlgorithm, | ||||||
|     pub replay_list: SplaySet<P::PacketIndex>, |     pub replay_window_head: u64, | ||||||
|  |     pub replay_window: FixedBitSet, | ||||||
|     pub session_encr_key: Vec<u8>, |     pub session_encr_key: Vec<u8>, | ||||||
|     pub session_salt_key: Vec<u8>, |     pub session_salt_key: Vec<u8>, | ||||||
|     pub session_auth_key: Vec<u8>, |     pub session_auth_key: Vec<u8>, | ||||||
|  | @ -329,7 +330,8 @@ where | ||||||
|             key_derivation_rate: 0, |             key_derivation_rate: 0, | ||||||
|             encryption: EncryptionAlgorithm::default(), |             encryption: EncryptionAlgorithm::default(), | ||||||
|             authentication: AuthenticationAlgorithm::default(), |             authentication: AuthenticationAlgorithm::default(), | ||||||
|             replay_list: SplaySet::new(), |             replay_window_head: 0, | ||||||
|  |             replay_window: FixedBitSet::with_capacity(128), | ||||||
|             session_encr_key: vec![0; 128 / 8], |             session_encr_key: vec![0; 128 / 8], | ||||||
|             session_salt_key: vec![0; 112 / 8], |             session_salt_key: vec![0; 112 / 8], | ||||||
|             session_auth_key: vec![0; 160 / 8], |             session_auth_key: vec![0; 160 / 8], | ||||||
|  | @ -461,14 +463,40 @@ where | ||||||
|         self.update_session_keys(index); |         self.update_session_keys(index); | ||||||
| 
 | 
 | ||||||
|         // Step 5: Replay protection and authentication
 |         // Step 5: Replay protection and authentication
 | ||||||
|         // TODO: replay protection
 |         let idx = u64::from(index); | ||||||
|  |         let window_size = self.replay_window.len() as u64; | ||||||
|  |         if idx <= self.replay_window_head { | ||||||
|  |             track_assert!( | ||||||
|  |                 idx + window_size > self.replay_window_head, | ||||||
|  |                 ErrorKind::Invalid | ||||||
|  |             ); | ||||||
|  |             track_assert!( | ||||||
|  |                 !self.replay_window[(idx % window_size) as usize], | ||||||
|  |                 ErrorKind::Invalid | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|         track_try!(self.authenticate(packet)); |         track_try!(self.authenticate(packet)); | ||||||
| 
 | 
 | ||||||
|         // Step 6: Decryption
 |         // Step 6: Decryption
 | ||||||
|         let result = track_try!(self.decrypt(packet, index)); |         let result = track_try!(self.decrypt(packet, index)); | ||||||
| 
 | 
 | ||||||
|         // Step 7: Update ROC, highest sequence number and replay protection
 |         // Step 7: Update ROC, highest sequence number and replay protection
 | ||||||
|         // TODO: replay protection
 |         if idx > self.replay_window_head { | ||||||
|  |             if idx - self.replay_window_head >= window_size { | ||||||
|  |                 self.replay_window.clear() | ||||||
|  |             } else { | ||||||
|  |                 let start = ((self.replay_window_head + 1) % window_size) as usize; | ||||||
|  |                 let end = (idx % window_size) as usize; | ||||||
|  |                 if start > end { | ||||||
|  |                     self.replay_window.set_range(start.., false); | ||||||
|  |                     self.replay_window.set_range(..end, false); | ||||||
|  |                 } else { | ||||||
|  |                     self.replay_window.set_range(start..end, false); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             self.replay_window_head = idx; | ||||||
|  |         } | ||||||
|  |         self.replay_window.insert((idx % window_size) as usize); | ||||||
|         P::update_highest_recv_index(self, index); |         P::update_highest_recv_index(self, index); | ||||||
| 
 | 
 | ||||||
|         Ok(result) |         Ok(result) | ||||||
|  | @ -790,4 +818,64 @@ mod test { | ||||||
|         assert_ne!(encrypted_1, encrypted_3); |         assert_ne!(encrypted_1, encrypted_3); | ||||||
|         assert_ne!(encrypted_2, encrypted_3); |         assert_ne!(encrypted_2, encrypted_3); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn rtp_does_not_allow_packet_replay() { | ||||||
|  |         let mut dec_context = Context::new_srtp(&TEST_MASTER_KEY, &TEST_MASTER_SALT); | ||||||
|  |         assert!(dec_context.process_incoming(TEST_SRTP_PACKET).is_ok()); | ||||||
|  |         assert!(dec_context.process_incoming(TEST_SRTP_PACKET).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(TEST_SRTP_PACKET).is_err()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn rtcp_does_not_allow_packet_replay() { | ||||||
|  |         let mut dec_context = Context::new_srtcp(&TEST_MASTER_KEY, &TEST_MASTER_SALT); | ||||||
|  |         assert!(dec_context.process_incoming(TEST_SRTCP_PACKET).is_ok()); | ||||||
|  |         assert!(dec_context.process_incoming(TEST_SRTCP_PACKET).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(TEST_SRTCP_PACKET).is_err()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn rtcp_does_not_allow_delayed_packet_replay() { | ||||||
|  |         let mut dec_context = Context::new_srtcp(&TEST_MASTER_KEY, &TEST_MASTER_SALT); | ||||||
|  |         let decrypted = dec_context.process_incoming(TEST_SRTCP_PACKET).unwrap(); | ||||||
|  | 
 | ||||||
|  |         let mut enc_context = Context::new_srtcp(&TEST_MASTER_KEY, &TEST_MASTER_SALT); | ||||||
|  |         const N: usize = 10; | ||||||
|  |         let encs: Vec<_> = (0..N) | ||||||
|  |             .map(|_| enc_context.process_outgoing(&decrypted).unwrap()) | ||||||
|  |             .collect(); | ||||||
|  | 
 | ||||||
|  |         let mut dec_context = Context::new_srtcp(&TEST_MASTER_KEY, &TEST_MASTER_SALT); | ||||||
|  |         dec_context.replay_window = FixedBitSet::with_capacity(4); | ||||||
|  | 
 | ||||||
|  |         assert!(dec_context.process_incoming(&encs[0]).is_ok()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[1]).is_ok()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[2]).is_ok()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[3]).is_ok()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[4]).is_ok()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[5]).is_ok()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[0]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[1]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[2]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[3]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[4]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[5]).is_err()); | ||||||
|  | 
 | ||||||
|  |         assert!(dec_context.process_incoming(&encs[7]).is_ok()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[6]).is_ok()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[3]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[4]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[5]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[6]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[7]).is_err()); | ||||||
|  | 
 | ||||||
|  |         assert!(dec_context.process_incoming(&encs[9]).is_ok()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[8]).is_ok()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[5]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[6]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[7]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[8]).is_err()); | ||||||
|  |         assert!(dec_context.process_incoming(&encs[9]).is_err()); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue