Test (de)serialization of DirSync::Resp

Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
Luca Fulchir 2023-06-09 21:58:33 +02:00
parent a32dfe098f
commit faaf8762c7
Signed by: luca.fulchir
GPG Key ID: 8F6440603D13A78E
7 changed files with 142 additions and 57 deletions

View File

@ -20,8 +20,6 @@ use crate::{
}, },
}; };
use ::arrayref::array_mut_ref;
// TODO: merge with crate::enc::sym::Nonce // TODO: merge with crate::enc::sym::Nonce
/// random nonce /// random nonce
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
@ -61,10 +59,10 @@ pub enum DirSync {
impl DirSync { impl DirSync {
/// actual length of the dirsync handshake data /// 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 { match self {
DirSync::Req(req) => req.len(), DirSync::Req(req) => req.len(),
DirSync::Resp(resp) => resp.len(), DirSync::Resp(resp) => resp.len(head_len, tag_len),
} }
} }
/// Serialize into raw bytes /// Serialize into raw bytes
@ -394,25 +392,31 @@ impl RespInner {
} }
} }
/// parse the cleartext /// 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 { let clear = match self {
RespInner::CipherText(len) => { RespInner::CipherText(len) => {
assert!( assert!(
*len == raw.len(), *len > raw.len(),
"DirSync::RespInner::CipherText length mismatch" "DirSync::RespInner::CipherText length mismatch"
); );
match RespData::deserialize(raw) { match RespData::deserialize(raw) {
Ok(clear) => clear, Ok(clear) => clear,
Err(_) => return, Err(e) => return Err(e),
} }
} }
_ => return, _ => return Err(Error::Parsing),
}; };
*self = RespInner::ClearText(clear); *self = RespInner::ClearText(clear);
Ok(())
} }
/// serialize, but only if ciphertext /// Serialize the still cleartext data
pub fn serialize(&self, out: &mut [u8]) { 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); return Err(Error::NotEnoughData);
} }
let client_key_id: KeyID = 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 { Ok(HandshakeData::DirSync(DirSync::Resp(Self {
client_key_id, client_key_id,
data: RespInner::CipherText(raw[KeyID::len()..].len()), data: RespInner::CipherText(raw[KeyID::len()..].len()),
@ -444,7 +448,9 @@ impl Resp {
/// return the offset of the encrypted data /// return the offset of the encrypted data
/// NOTE: starts from the beginning of the fenrir packet /// NOTE: starts from the beginning of the fenrir packet
pub fn encrypted_offset(&self) -> usize { 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 /// return the total length of the cleartext data
pub fn encrypted_length(&self) -> usize { pub fn encrypted_length(&self) -> usize {
@ -454,29 +460,21 @@ impl Resp {
} }
} }
/// Total length of the response handshake /// Total length of the response handshake
pub fn len(&self) -> usize { pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize {
KeyID::len() + self.data.len() KeyID::len() + head_len.0 + self.data.len() + tag_len.0
} }
/// Serialize into raw bytes /// Serialize into raw bytes
/// NOTE: assumes that there is exactly as much buffer as needed /// NOTE: assumes that there is exactly as much buffer as needed
/// NOTE: assumes that the data is *ClearText*
pub fn serialize( pub fn serialize(
&self, &self,
head_len: HeadLen, head_len: HeadLen,
tag_len: TagLen, _tag_len: TagLen,
out: &mut [u8], out: &mut [u8],
) { ) {
assert!( out[0..2].copy_from_slice(&self.client_key_id.0.to_le_bytes());
out.len() == KeyID::len() + self.data.len(), let start_data = 2 + head_len.0;
"DirSync Resp: not enough buffer to serialize" let end_data = start_data + self.data.len();
); self.data.serialize(&mut out[start_data..end_data]);
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);
} }
} }
@ -494,30 +492,54 @@ pub struct RespData {
} }
impl RespData { impl RespData {
const NONCE_LEN: usize = ::core::mem::size_of::<Nonce>();
/// Return the expected length for buffer allocation /// Return the expected length for buffer allocation
pub fn len() -> usize { 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 /// Serialize the data into a buffer
/// NOTE: assumes that there is exactly asa much buffer as needed /// NOTE: assumes that there is exactly asa much buffer as needed
pub fn serialize(&self, out: &mut [u8]) { pub fn serialize(&self, out: &mut [u8]) {
assert!(out.len() == Self::len(), "wrong buffer size");
let mut start = 0; 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); out[start..end].copy_from_slice(&self.client_nonce.0);
start = end; start = end;
end = end + Self::NONCE_LEN; end = end + ID::len();
self.id.serialize(&mut out[start..end]); self.id.serialize(&mut out[start..end]);
start = end; start = end;
end = end + Self::NONCE_LEN; end = end + ID::len();
self.service_connection_id.serialize(&mut out[start..end]); self.service_connection_id.serialize(&mut out[start..end]);
start = end; start = end;
end = end + Self::NONCE_LEN; end = end + Secret::len();
out[start..end].copy_from_slice(self.service_key.as_ref()); out[start..end].copy_from_slice(self.service_key.as_ref());
} }
/// Parse the cleartext raw data /// Parse the cleartext raw data
pub fn deserialize(raw: &[u8]) -> Result<Self, Error> { pub fn deserialize(raw: &[u8]) -> Result<Self, Error> {
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,
})
} }
} }

View File

@ -179,9 +179,9 @@ pub enum HandshakeData {
impl HandshakeData { impl HandshakeData {
/// actual length of the handshake data /// actual length of the handshake data
pub fn len(&self) -> usize { pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize {
match self { match self {
HandshakeData::DirSync(d) => d.len(), HandshakeData::DirSync(d) => d.len(head_len, tag_len),
} }
} }
/// Serialize into raw bytes /// Serialize into raw bytes
@ -242,8 +242,10 @@ impl Handshake {
} }
} }
/// return the total length of the handshake /// return the total length of the handshake
pub fn len(&self) -> usize { pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize {
ProtocolVersion::len() + HandshakeKind::len() + self.data.len() ProtocolVersion::len()
+ HandshakeKind::len()
+ self.data.len(head_len, tag_len)
} }
const MIN_PKT_LEN: usize = 8; const MIN_PKT_LEN: usize = 8;
/// Parse the packet and return the parsed handshake /// Parse the packet and return the parsed handshake

View File

@ -7,7 +7,6 @@ use crate::{
#[test] #[test]
fn test_handshake_dirsync_req() { fn test_handshake_dirsync_req() {
let rand = enc::Random::new(); let rand = enc::Random::new();
let secret = enc::Secret::new_rand(&rand);
let cipher = enc::sym::CipherKind::XChaCha20Poly1305; let cipher = enc::sym::CipherKind::XChaCha20Poly1305;
let (_, exchange_key) = let (_, exchange_key) =
@ -43,8 +42,10 @@ fn test_handshake_dirsync_req() {
}, },
))); )));
let mut bytes = Vec::<u8>::with_capacity(h_req.len()); let mut bytes = Vec::<u8>::with_capacity(
bytes.resize(h_req.len(), 0); 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); h_req.serialize(cipher.nonce_len(), cipher.tag_len(), &mut bytes);
let mut deserialized = match Handshake::deserialize(&bytes) { let mut deserialized = match Handshake::deserialize(&bytes) {
@ -70,3 +71,55 @@ fn test_handshake_dirsync_req() {
"DirSync Req (de)serialization not working", "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::<u8>::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",
);
}

View File

@ -58,11 +58,10 @@ impl ConnectionID {
} }
/// write the ID to a buffer /// write the ID to a buffer
pub fn serialize(&self, out: &mut [u8]) { pub fn serialize(&self, out: &mut [u8]) {
assert!(out.len() == 8, "out buffer must be 8 bytes");
match self { match self {
ConnectionID::Handshake => out[..].copy_from_slice(&[0; 8]), ConnectionID::Handshake => out[..8].copy_from_slice(&[0; 8]),
ConnectionID::ID(id) => { 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 { impl PacketData {
/// total length of the data in bytes /// 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 { match self {
PacketData::Handshake(h) => h.len(), PacketData::Handshake(h) => h.len(head_len, tag_len),
PacketData::Raw(len) => *len, PacketData::Raw(len) => *len,
} }
} }
@ -113,7 +112,10 @@ impl PacketData {
tag_len: TagLen, tag_len: TagLen,
out: &mut [u8], 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 { match self {
PacketData::Handshake(h) => h.serialize(head_len, tag_len, out), PacketData::Handshake(h) => h.serialize(head_len, tag_len, out),
PacketData::Raw(_) => { PacketData::Raw(_) => {
@ -148,8 +150,8 @@ impl Packet {
}) })
} }
/// get the total length of the packet /// get the total length of the packet
pub fn len(&self) -> usize { pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize {
ConnectionID::len() + self.data.len() ConnectionID::len() + self.data.len(head_len, tag_len)
} }
/// serialize packet into buffer /// serialize packet into buffer
/// NOTE: assumes that there is exactly asa much buffer as needed /// NOTE: assumes that there is exactly asa much buffer as needed

View File

@ -94,6 +94,10 @@ impl ::core::fmt::Debug for Secret {
} }
impl Secret { impl Secret {
/// return the length of the serialized secret
pub const fn len() -> usize {
32
}
/// New randomly generated secret /// New randomly generated secret
pub fn new_rand(rand: &Random) -> Self { pub fn new_rand(rand: &Random) -> Self {
let mut ret = Self([0; 32]); let mut ret = Self([0; 32]);
@ -110,6 +114,11 @@ impl From<[u8; 32]> for Secret {
Self(shared_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 { impl From<::x25519_dalek::SharedSecret> for Secret {
fn from(shared_secret: ::x25519_dalek::SharedSecret) -> Self { fn from(shared_secret: ::x25519_dalek::SharedSecret) -> Self {

View File

@ -184,11 +184,7 @@ impl HandshakeTracker {
&mut handshake_raw[req.encrypted_offset()..], &mut handshake_raw[req.encrypted_offset()..],
) { ) {
Ok(cleartext) => { Ok(cleartext) => {
if let Err(e) = req.data.deserialize_as_cleartext(cleartext)?;
req.data.deserialize_as_cleartext(cleartext)
{
return Err(e.into());
}
} }
Err(e) => { Err(e) => {
return Err(handshake::Error::Key(e).into()); return Err(handshake::Error::Key(e).into());
@ -223,7 +219,7 @@ impl HandshakeTracker {
..(resp.encrypted_offset() + resp.encrypted_length())]; ..(resp.encrypted_offset() + resp.encrypted_length())];
match cipher_recv.decrypt(aad, &mut raw_data) { match cipher_recv.decrypt(aad, &mut raw_data) {
Ok(cleartext) => { Ok(cleartext) => {
resp.data.deserialize_as_cleartext(&cleartext) resp.data.deserialize_as_cleartext(&cleartext)?;
} }
Err(e) => { Err(e) => {
return Err(handshake::Error::Key(e).into()); return Err(handshake::Error::Key(e).into());

View File

@ -546,7 +546,8 @@ impl Worker {
id: ID::new_handshake(), id: ID::new_handshake(),
data: PacketData::Handshake(resp_handshake), data: PacketData::Handshake(resp_handshake),
}; };
let mut raw_out = Vec::<u8>::with_capacity(packet.len()); let mut raw_out =
Vec::<u8>::with_capacity(packet.len(head_len, tag_len));
packet.serialize(head_len, tag_len, &mut raw_out); packet.serialize(head_len, tag_len, &mut raw_out);
if let Err(e) = auth_conn.cipher_send.encrypt( if let Err(e) = auth_conn.cipher_send.encrypt(