Add SRT(C)P replay protection

dtls-srtp
Jonas Herzig 2018-11-09 13:33:41 +01:00
parent aa4927724b
commit bda8ce2295
3 changed files with 95 additions and 7 deletions

View File

@ -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"

View File

@ -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};

View File

@ -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());
}
} }