added counter randomization

This commit is contained in:
mamoniot 2023-03-22 19:14:25 -04:00
parent 9e4e099843
commit 562631f18d
No known key found for this signature in database
GPG key ID: ADCCDBBE0E3D3B3B
2 changed files with 24 additions and 18 deletions

View file

@ -45,6 +45,8 @@ impl<Fragment, const MAX_FRAGMENTS: usize> Fragged<Fragment, MAX_FRAGMENTS> {
unsafe { zeroed() } unsafe { zeroed() }
} }
/// Returns the counter value associated with the packet currently being assembled.
/// If no packet is currently being assembled it returns 0.
#[inline(always)] #[inline(always)]
pub fn counter(&self) -> u64 { pub fn counter(&self) -> u64 {
self.counter self.counter

View file

@ -198,7 +198,7 @@ impl<Application: ApplicationLayer> Context<Application> {
PACKET_TYPE_ALICE_NOISE_XK_INIT, PACKET_TYPE_ALICE_NOISE_XK_INIT,
None, None,
0, 0,
1, random::next_u64_secure(),
None, None,
); );
} }
@ -378,7 +378,7 @@ impl<Application: ApplicationLayer> Context<Application> {
PACKET_TYPE_ALICE_NOISE_XK_INIT, PACKET_TYPE_ALICE_NOISE_XK_INIT,
None, None,
0, 0,
1, random::next_u64_secure(),
None, None,
)?; )?;
} }
@ -450,7 +450,7 @@ impl<Application: ApplicationLayer> Context<Application> {
let (key_index, packet_type, fragment_count, fragment_no, incoming_counter) = parse_packet_header(&incoming_physical_packet); let (key_index, packet_type, fragment_count, fragment_no, incoming_counter) = parse_packet_header(&incoming_physical_packet);
if session.check_receive_window(incoming_counter) { if session.check_receive_window(incoming_counter) {
let (assembled_packet, incoming_packet_buf_arr); let assembled_packet;
let incoming_packet = if fragment_count > 1 { let incoming_packet = if fragment_count > 1 {
assembled_packet = session.defrag[(incoming_counter as usize) % COUNTER_WINDOW_MAX_OOO] assembled_packet = session.defrag[(incoming_counter as usize) % COUNTER_WINDOW_MAX_OOO]
.lock() .lock()
@ -462,8 +462,7 @@ impl<Application: ApplicationLayer> Context<Application> {
return Ok(ReceiveResult::Ok(Some(session))); return Ok(ReceiveResult::Ok(Some(session)));
} }
} else { } else {
incoming_packet_buf_arr = [incoming_physical_packet_buf]; std::array::from_ref(&incoming_physical_packet_buf)
&incoming_packet_buf_arr
}; };
return self.process_complete_incoming_packet( return self.process_complete_incoming_packet(
@ -491,7 +490,7 @@ impl<Application: ApplicationLayer> Context<Application> {
.decrypt_block_in_place(&mut incoming_physical_packet[HEADER_PROTECT_ENCRYPT_START..HEADER_PROTECT_ENCRYPT_END]); .decrypt_block_in_place(&mut incoming_physical_packet[HEADER_PROTECT_ENCRYPT_START..HEADER_PROTECT_ENCRYPT_END]);
let (key_index, packet_type, fragment_count, fragment_no, incoming_counter) = parse_packet_header(&incoming_physical_packet); let (key_index, packet_type, fragment_count, fragment_no, incoming_counter) = parse_packet_header(&incoming_physical_packet);
let (assembled_packet, incoming_packet_buf_arr); let assembled_packet;
let incoming_packet = if fragment_count > 1 { let incoming_packet = if fragment_count > 1 {
assembled_packet = incoming.defrag[(incoming_counter as usize) % COUNTER_WINDOW_MAX_OOO] assembled_packet = incoming.defrag[(incoming_counter as usize) % COUNTER_WINDOW_MAX_OOO]
.lock() .lock()
@ -503,8 +502,7 @@ impl<Application: ApplicationLayer> Context<Application> {
return Ok(ReceiveResult::Ok(None)); return Ok(ReceiveResult::Ok(None));
} }
} else { } else {
incoming_packet_buf_arr = [incoming_physical_packet_buf]; std::array::from_ref(&incoming_physical_packet_buf)
&incoming_packet_buf_arr
}; };
return self.process_complete_incoming_packet( return self.process_complete_incoming_packet(
@ -532,19 +530,25 @@ impl<Application: ApplicationLayer> Context<Application> {
let mut hasher = self.defrag_hasher.build_hasher(); let mut hasher = self.defrag_hasher.build_hasher();
source.hash(&mut hasher); source.hash(&mut hasher);
hasher.write_u64(incoming_counter); hasher.write_u64(incoming_counter);
let offer_id = hasher.finish(); let hashed_counter = hasher.finish();
let idx0 = (offer_id as usize)%MAX_INCOMPLETE_SESSION_QUEUE_SIZE; let idx0 = (hashed_counter as usize)%MAX_INCOMPLETE_SESSION_QUEUE_SIZE;
let idx1 = (offer_id as usize)/MAX_INCOMPLETE_SESSION_QUEUE_SIZE%MAX_INCOMPLETE_SESSION_QUEUE_SIZE; let idx1 = (hashed_counter as usize)/MAX_INCOMPLETE_SESSION_QUEUE_SIZE%MAX_INCOMPLETE_SESSION_QUEUE_SIZE;
// Open hash lookup of just 2 slots.
// By only checking 2 slots we avoid an expensive hash table lookup while also minimizing the chance that 2 offers collide.
// To DOS, an adversary would either need to volumetrically spam the defrag table to keep all slots full
// or replay Alice's packet header before Alice's packet is fully assembled.
// Since Alice's packet header has a randomly generated counter value replaying it in time is very hard.
let mut slot0 = self.defrag[idx0].lock().unwrap(); let mut slot0 = self.defrag[idx0].lock().unwrap();
if slot0.counter() == offer_id { if slot0.counter() == hashed_counter {
assembled = slot0.assemble(offer_id, incoming_physical_packet_buf, fragment_no, fragment_count); assembled = slot0.assemble(hashed_counter, incoming_physical_packet_buf, fragment_no, fragment_count);
} else { } else {
let mut slot1 = self.defrag[idx1].lock().unwrap(); let mut slot1 = self.defrag[idx1].lock().unwrap();
if slot1.counter() == offer_id || slot1.counter() == 0 { if slot1.counter() == hashed_counter || slot1.counter() == 0 {
assembled = slot1.assemble(offer_id, incoming_physical_packet_buf, fragment_no, fragment_count); assembled = slot1.assemble(hashed_counter, incoming_physical_packet_buf, fragment_no, fragment_count);
} else { } else {
assembled = slot0.assemble(offer_id, incoming_physical_packet_buf, fragment_no, fragment_count); // Both slots are full so kick out the unassembled packet in slot0 to make more room.
assembled = slot0.assemble(hashed_counter, incoming_physical_packet_buf, fragment_no, fragment_count);
} }
} }
@ -563,7 +567,7 @@ impl<Application: ApplicationLayer> Context<Application> {
&mut check_allow_incoming_session, &mut check_allow_incoming_session,
&mut check_accept_session, &mut check_accept_session,
data_buf, data_buf,
incoming_counter, 1,// The incoming_counter on init packets is only meant for DOS resistant defragmentation, we do not want to use it for anything noise related.
incoming_packet, incoming_packet,
packet_type, packet_type,
None, None,
@ -702,7 +706,7 @@ impl<Application: ApplicationLayer> Context<Application> {
* identity, then responds with his ephemeral keys. * identity, then responds with his ephemeral keys.
*/ */
if incoming_counter != 1 || session.is_some() || incoming.is_some() { if session.is_some() || incoming.is_some() {
return Err(Error::OutOfSequence); return Err(Error::OutOfSequence);
} }
if pkt_assembled.len() != AliceNoiseXKInit::SIZE { if pkt_assembled.len() != AliceNoiseXKInit::SIZE {