From faaf8762c7c012ec6969d7caf1d1a9f8fd026991 Mon Sep 17 00:00:00 2001 From: Luca Fulchir Date: Fri, 9 Jun 2023 21:58:33 +0200 Subject: [PATCH] Test (de)serialization of DirSync::Resp Signed-off-by: Luca Fulchir --- src/connection/handshake/dirsync.rs | 92 ++++++++++++++++++----------- src/connection/handshake/mod.rs | 10 ++-- src/connection/handshake/tests.rs | 59 +++++++++++++++++- src/connection/packet.rs | 18 +++--- src/enc/mod.rs | 9 +++ src/inner/mod.rs | 8 +-- src/inner/worker.rs | 3 +- 7 files changed, 142 insertions(+), 57 deletions(-) diff --git a/src/connection/handshake/dirsync.rs b/src/connection/handshake/dirsync.rs index 34b2819..8023ed6 100644 --- a/src/connection/handshake/dirsync.rs +++ b/src/connection/handshake/dirsync.rs @@ -20,8 +20,6 @@ use crate::{ }, }; -use ::arrayref::array_mut_ref; - // TODO: merge with crate::enc::sym::Nonce /// random nonce #[derive(Debug, Clone, Copy, PartialEq)] @@ -61,10 +59,10 @@ pub enum DirSync { impl DirSync { /// actual length of the dirsync handshake data - pub fn len(&self) -> usize { + pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize { match self { DirSync::Req(req) => req.len(), - DirSync::Resp(resp) => resp.len(), + DirSync::Resp(resp) => resp.len(head_len, tag_len), } } /// Serialize into raw bytes @@ -394,25 +392,31 @@ impl RespInner { } } /// parse the cleartext - pub fn deserialize_as_cleartext(&mut self, raw: &[u8]) { + pub fn deserialize_as_cleartext( + &mut self, + raw: &[u8], + ) -> Result<(), Error> { let clear = match self { RespInner::CipherText(len) => { assert!( - *len == raw.len(), + *len > raw.len(), "DirSync::RespInner::CipherText length mismatch" ); match RespData::deserialize(raw) { Ok(clear) => clear, - Err(_) => return, + Err(e) => return Err(e), } } - _ => return, + _ => return Err(Error::Parsing), }; *self = RespInner::ClearText(clear); + Ok(()) } - /// serialize, but only if ciphertext + /// Serialize the still cleartext data pub fn serialize(&self, out: &mut [u8]) { - todo!() + if let RespInner::ClearText(clear) = &self { + clear.serialize(out); + } } } @@ -432,7 +436,7 @@ impl super::HandshakeParsing for Resp { return Err(Error::NotEnoughData); } let client_key_id: KeyID = - KeyID(u16::from_le_bytes(raw[0..1].try_into().unwrap())); + KeyID(u16::from_le_bytes(raw[0..2].try_into().unwrap())); Ok(HandshakeData::DirSync(DirSync::Resp(Self { client_key_id, data: RespInner::CipherText(raw[KeyID::len()..].len()), @@ -444,7 +448,9 @@ impl Resp { /// return the offset of the encrypted data /// NOTE: starts from the beginning of the fenrir packet pub fn encrypted_offset(&self) -> usize { - ProtocolVersion::len() + KeyID::len() + ProtocolVersion::len() + + crate::connection::handshake::HandshakeID::len() + + KeyID::len() } /// return the total length of the cleartext data pub fn encrypted_length(&self) -> usize { @@ -454,29 +460,21 @@ impl Resp { } } /// Total length of the response handshake - pub fn len(&self) -> usize { - KeyID::len() + self.data.len() + pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize { + KeyID::len() + head_len.0 + self.data.len() + tag_len.0 } /// Serialize into raw bytes /// NOTE: assumes that there is exactly as much buffer as needed - /// NOTE: assumes that the data is *ClearText* pub fn serialize( &self, head_len: HeadLen, - tag_len: TagLen, + _tag_len: TagLen, out: &mut [u8], ) { - assert!( - out.len() == KeyID::len() + self.data.len(), - "DirSync Resp: not enough buffer to serialize" - ); - self.client_key_id.serialize(array_mut_ref![out, 0, 2]); - let end_data = (2 + self.data.len()) - tag_len.0; - self.data.serialize(&mut out[(2 + head_len.0)..end_data]); - } - /// Set the cleartext data after it was parsed - pub fn set_data(&mut self, data: RespData) { - self.data = RespInner::ClearText(data); + out[0..2].copy_from_slice(&self.client_key_id.0.to_le_bytes()); + let start_data = 2 + head_len.0; + let end_data = start_data + self.data.len(); + self.data.serialize(&mut out[start_data..end_data]); } } @@ -494,30 +492,54 @@ pub struct RespData { } impl RespData { - const NONCE_LEN: usize = ::core::mem::size_of::(); /// Return the expected length for buffer allocation pub fn len() -> usize { - Self::NONCE_LEN + ID::len() + ID::len() + 32 + Nonce::len() + ID::len() + ID::len() + Secret::len() } /// Serialize the data into a buffer /// NOTE: assumes that there is exactly asa much buffer as needed pub fn serialize(&self, out: &mut [u8]) { - assert!(out.len() == Self::len(), "wrong buffer size"); let mut start = 0; - let mut end = Self::NONCE_LEN; + let mut end = Nonce::len(); out[start..end].copy_from_slice(&self.client_nonce.0); start = end; - end = end + Self::NONCE_LEN; + end = end + ID::len(); self.id.serialize(&mut out[start..end]); start = end; - end = end + Self::NONCE_LEN; + end = end + ID::len(); self.service_connection_id.serialize(&mut out[start..end]); start = end; - end = end + Self::NONCE_LEN; + end = end + Secret::len(); out[start..end].copy_from_slice(self.service_key.as_ref()); } /// Parse the cleartext raw data pub fn deserialize(raw: &[u8]) -> Result { - todo!(); + let raw_sized: &[u8; 16] = raw[..Nonce::len()].try_into().unwrap(); + let client_nonce: Nonce = raw_sized.into(); + let end = Nonce::len() + ID::len(); + let id: ID = + u64::from_le_bytes(raw[Nonce::len()..end].try_into().unwrap()) + .into(); + if id.is_handshake() { + return Err(Error::Parsing); + } + let parsed = end; + let end = parsed + ID::len(); + let service_connection_id: ID = + u64::from_le_bytes(raw[parsed..end].try_into().unwrap()).into(); + if service_connection_id.is_handshake() { + return Err(Error::Parsing); + } + let parsed = end; + let end = parsed + Secret::len(); + let raw_secret: &[u8; 32] = raw[parsed..end].try_into().unwrap(); + let service_key = raw_secret.into(); + + Ok(Self { + client_nonce, + id, + service_connection_id, + service_key, + }) } } diff --git a/src/connection/handshake/mod.rs b/src/connection/handshake/mod.rs index 8796566..6322e79 100644 --- a/src/connection/handshake/mod.rs +++ b/src/connection/handshake/mod.rs @@ -179,9 +179,9 @@ pub enum HandshakeData { impl HandshakeData { /// actual length of the handshake data - pub fn len(&self) -> usize { + pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize { match self { - HandshakeData::DirSync(d) => d.len(), + HandshakeData::DirSync(d) => d.len(head_len, tag_len), } } /// Serialize into raw bytes @@ -242,8 +242,10 @@ impl Handshake { } } /// return the total length of the handshake - pub fn len(&self) -> usize { - ProtocolVersion::len() + HandshakeKind::len() + self.data.len() + pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize { + ProtocolVersion::len() + + HandshakeKind::len() + + self.data.len(head_len, tag_len) } const MIN_PKT_LEN: usize = 8; /// Parse the packet and return the parsed handshake diff --git a/src/connection/handshake/tests.rs b/src/connection/handshake/tests.rs index c5f84dd..fc8b2b3 100644 --- a/src/connection/handshake/tests.rs +++ b/src/connection/handshake/tests.rs @@ -7,7 +7,6 @@ use crate::{ #[test] fn test_handshake_dirsync_req() { let rand = enc::Random::new(); - let secret = enc::Secret::new_rand(&rand); let cipher = enc::sym::CipherKind::XChaCha20Poly1305; let (_, exchange_key) = @@ -43,8 +42,10 @@ fn test_handshake_dirsync_req() { }, ))); - let mut bytes = Vec::::with_capacity(h_req.len()); - bytes.resize(h_req.len(), 0); + let mut bytes = Vec::::with_capacity( + h_req.len(cipher.nonce_len(), cipher.tag_len()), + ); + bytes.resize(h_req.len(cipher.nonce_len(), cipher.tag_len()), 0); h_req.serialize(cipher.nonce_len(), cipher.tag_len(), &mut bytes); let mut deserialized = match Handshake::deserialize(&bytes) { @@ -70,3 +71,55 @@ fn test_handshake_dirsync_req() { "DirSync Req (de)serialization not working", ); } +#[test] +fn test_handshake_dirsync_reqsp() { + let rand = enc::Random::new(); + let cipher = enc::sym::CipherKind::XChaCha20Poly1305; + + let service_key = enc::Secret::new_rand(&rand); + + let data = dirsync::RespInner::ClearText(dirsync::RespData { + client_nonce: dirsync::Nonce::new(&rand), + id: ID::ID(::core::num::NonZeroU64::new(424242).unwrap()), + service_connection_id: ID::ID( + ::core::num::NonZeroU64::new(434343).unwrap(), + ), + service_key, + }); + + let h_resp = Handshake::new(HandshakeData::DirSync( + dirsync::DirSync::Resp(dirsync::Resp { + client_key_id: KeyID(4444), + data, + }), + )); + + let mut bytes = Vec::::with_capacity( + h_resp.len(cipher.nonce_len(), cipher.tag_len()), + ); + bytes.resize(h_resp.len(cipher.nonce_len(), cipher.tag_len()), 0); + h_resp.serialize(cipher.nonce_len(), cipher.tag_len(), &mut bytes); + + let mut deserialized = match Handshake::deserialize(&bytes) { + Ok(deserialized) => deserialized, + Err(e) => { + assert!(false, "{}", e.to_string()); + return; + } + }; + if let HandshakeData::DirSync(dirsync::DirSync::Resp(r_a)) = + &mut deserialized.data + { + let enc_start = r_a.encrypted_offset() + cipher.nonce_len().0; + if let Err(e) = r_a.data.deserialize_as_cleartext( + &bytes[enc_start..(bytes.len() - cipher.tag_len().0)], + ) { + assert!(false, "DirSync Resp Inner serialize: {}", e.to_string()); + } + }; + + assert!( + deserialized == h_resp, + "DirSync Resp (de)serialization not working", + ); +} diff --git a/src/connection/packet.rs b/src/connection/packet.rs index b051594..925460c 100644 --- a/src/connection/packet.rs +++ b/src/connection/packet.rs @@ -58,11 +58,10 @@ impl ConnectionID { } /// write the ID to a buffer pub fn serialize(&self, out: &mut [u8]) { - assert!(out.len() == 8, "out buffer must be 8 bytes"); match self { - ConnectionID::Handshake => out[..].copy_from_slice(&[0; 8]), + ConnectionID::Handshake => out[..8].copy_from_slice(&[0; 8]), ConnectionID::ID(id) => { - out[..].copy_from_slice(&id.get().to_le_bytes()) + out[..8].copy_from_slice(&id.get().to_le_bytes()) } } } @@ -99,9 +98,9 @@ pub enum PacketData { impl PacketData { /// total length of the data in bytes - pub fn len(&self) -> usize { + pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize { match self { - PacketData::Handshake(h) => h.len(), + PacketData::Handshake(h) => h.len(head_len, tag_len), PacketData::Raw(len) => *len, } } @@ -113,7 +112,10 @@ impl PacketData { tag_len: TagLen, out: &mut [u8], ) { - assert!(self.len() == out.len(), "PacketData: wrong buffer length"); + assert!( + self.len(head_len, tag_len) == out.len(), + "PacketData: wrong buffer length" + ); match self { PacketData::Handshake(h) => h.serialize(head_len, tag_len, out), PacketData::Raw(_) => { @@ -148,8 +150,8 @@ impl Packet { }) } /// get the total length of the packet - pub fn len(&self) -> usize { - ConnectionID::len() + self.data.len() + pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize { + ConnectionID::len() + self.data.len(head_len, tag_len) } /// serialize packet into buffer /// NOTE: assumes that there is exactly asa much buffer as needed diff --git a/src/enc/mod.rs b/src/enc/mod.rs index ff1bb9f..9a01b98 100644 --- a/src/enc/mod.rs +++ b/src/enc/mod.rs @@ -94,6 +94,10 @@ impl ::core::fmt::Debug for Secret { } impl Secret { + /// return the length of the serialized secret + pub const fn len() -> usize { + 32 + } /// New randomly generated secret pub fn new_rand(rand: &Random) -> Self { let mut ret = Self([0; 32]); @@ -110,6 +114,11 @@ impl From<[u8; 32]> for Secret { Self(shared_secret) } } +impl From<&[u8; 32]> for Secret { + fn from(shared_secret: &[u8; 32]) -> Self { + Self(*shared_secret) + } +} impl From<::x25519_dalek::SharedSecret> for Secret { fn from(shared_secret: ::x25519_dalek::SharedSecret) -> Self { diff --git a/src/inner/mod.rs b/src/inner/mod.rs index faa7609..882e8c1 100644 --- a/src/inner/mod.rs +++ b/src/inner/mod.rs @@ -184,11 +184,7 @@ impl HandshakeTracker { &mut handshake_raw[req.encrypted_offset()..], ) { Ok(cleartext) => { - if let Err(e) = - req.data.deserialize_as_cleartext(cleartext) - { - return Err(e.into()); - } + req.data.deserialize_as_cleartext(cleartext)?; } Err(e) => { return Err(handshake::Error::Key(e).into()); @@ -223,7 +219,7 @@ impl HandshakeTracker { ..(resp.encrypted_offset() + resp.encrypted_length())]; match cipher_recv.decrypt(aad, &mut raw_data) { Ok(cleartext) => { - resp.data.deserialize_as_cleartext(&cleartext) + resp.data.deserialize_as_cleartext(&cleartext)?; } Err(e) => { return Err(handshake::Error::Key(e).into()); diff --git a/src/inner/worker.rs b/src/inner/worker.rs index 6d77c03..4f0459f 100644 --- a/src/inner/worker.rs +++ b/src/inner/worker.rs @@ -546,7 +546,8 @@ impl Worker { id: ID::new_handshake(), data: PacketData::Handshake(resp_handshake), }; - let mut raw_out = Vec::::with_capacity(packet.len()); + let mut raw_out = + Vec::::with_capacity(packet.len(head_len, tag_len)); packet.serialize(head_len, tag_len, &mut raw_out); if let Err(e) = auth_conn.cipher_send.encrypt(