diff --git a/homekit-controller/src/tlv8/data_types.rs b/homekit-controller/src/tlv8/data_types.rs new file mode 100644 index 0000000..d680087 --- /dev/null +++ b/homekit-controller/src/tlv8/data_types.rs @@ -0,0 +1,151 @@ +use super::Tlv; + +pub enum TlvType { + Method, + Identifier, + Salt, + PublicKey, + Proof, + EncryptedData, + State, + Error, + RetryDelay, + Certificate, + Signature, + Permissions, + PermissionRegularUser, + PermissionAdminUser, + FragmentData, + FragmentLast, + SessionID, +} + +impl From for u8 { + fn from(value: TlvType) -> Self { + match value { + TlvType::Method => 0, + TlvType::Identifier => 1, + TlvType::Salt => 2, + TlvType::PublicKey => 3, + TlvType::Proof => 4, + TlvType::EncryptedData => 5, + TlvType::State => 6, + TlvType::Error => 7, + TlvType::RetryDelay => 8, + TlvType::Certificate => 9, + TlvType::Signature => 10, + TlvType::Permissions => 11, + TlvType::PermissionRegularUser => 0, + TlvType::PermissionAdminUser => 1, + TlvType::FragmentData => 12, + TlvType::FragmentLast => 13, + TlvType::SessionID => 14, + } + } +} + +#[allow(unused)] +#[derive(Debug, Clone)] +pub enum TlvData { + Bytes(Vec), + Tlv8(Vec), + Integer(TlvInteger), + Float(f64), + String(String), + Uint(TlvUnsignedInteger), +} + +#[derive(Debug, Clone)] +pub enum TlvInteger { + OneByte(i8), + TwoBytes(i16), + FourBytes(i32), + EightBytes(i64), +} + +impl TlvInteger { + pub fn to_le_bytes(&self) -> Vec { + match self { + TlvInteger::OneByte(v) => v.to_le_bytes().to_vec(), + TlvInteger::TwoBytes(v) => v.to_le_bytes().to_vec(), + TlvInteger::FourBytes(v) => v.to_le_bytes().to_vec(), + TlvInteger::EightBytes(v) => v.to_le_bytes().to_vec(), + } + } +} + +impl From for TlvData +where + T: Into, +{ + fn from(value: T) -> Self { + Self::Integer(value.into()) + } +} + +impl From for TlvInteger { + fn from(value: i8) -> Self { + Self::OneByte(value) + } +} + +impl From for TlvInteger { + fn from(value: i16) -> Self { + Self::TwoBytes(value) + } +} + +impl From for TlvInteger { + fn from(value: i32) -> Self { + Self::FourBytes(value) + } +} + +impl From for TlvInteger { + fn from(value: i64) -> Self { + Self::EightBytes(value) + } +} + +#[derive(Debug, Clone)] +pub enum TlvUnsignedInteger { + OneByte(u8), + TwoBytes(u16), + FourBytes(u32), + EightBytes(u64), +} + +impl TlvUnsignedInteger { + pub fn to_le_bytes(&self) -> Vec { + match self { + TlvUnsignedInteger::OneByte(v) => v.to_le_bytes().to_vec(), + TlvUnsignedInteger::TwoBytes(v) => v.to_le_bytes().to_vec(), + TlvUnsignedInteger::FourBytes(v) => v.to_le_bytes().to_vec(), + TlvUnsignedInteger::EightBytes(v) => v.to_le_bytes().to_vec(), + } + } +} + +impl From for TlvUnsignedInteger { + fn from(value: u8) -> Self { + Self::OneByte(value) + } +} + +impl From for TlvUnsignedInteger { + fn from(value: u16) -> Self { + Self::TwoBytes(value) + } +} + +impl From for TlvUnsignedInteger { + fn from(value: u32) -> Self { + Self::FourBytes(value) + } +} + +impl From for TlvUnsignedInteger { + fn from(value: u64) -> Self { + Self::EightBytes(value) + } +} diff --git a/homekit-controller/src/tlv8/mod.rs b/homekit-controller/src/tlv8/mod.rs new file mode 100644 index 0000000..ac6f6a5 --- /dev/null +++ b/homekit-controller/src/tlv8/mod.rs @@ -0,0 +1,258 @@ +mod data_types; + +use std::collections::HashMap; + +#[allow(unused)] +pub use data_types::{TlvData, TlvInteger, TlvType, TlvUnsignedInteger}; + +use thiserror::Error; + +pub fn decode(data: &[u8]) -> Result>, TlvError> { + let mut tlvs = HashMap::new(); + let mut data = data; + + while data.len() > 0 { + let tlv_len = (data[1] as usize) + 2; + let current; + (current, data) = data.split_at(tlv_len); + + if data.len() < 3 { + return Err(TlvError::TooShort); + } + + if data.len() < tlv_len { + return Err(TlvError::WrongLength); + } + + let tlv_type = current[0]; + let tlv_data = current[2..].to_vec(); + tlvs.insert(tlv_type, tlv_data); + } + Ok(tlvs) +} + +fn decode_single(data: &[u8]) -> Result<(u8, Vec), TlvError> { + let tlv_type = data[0]; + let tlv_len = data[1]; + let data = &data[2..]; + Ok((tlv_type, if tlv_len == 0 { vec![] } else { data.to_vec() })) +} + +#[derive(Debug, Error)] +pub enum TlvError { + #[error("too short")] + TooShort, + #[error("wrong length")] + WrongLength, +} + +pub trait TlvEncode { + fn encode(&self) -> Vec; +} + +// impl TlvEncode for &[(u8, Vec); N] { +// fn encode(&self) -> Vec { +// (*self as &[(u8, Vec)]).encode() +// } +// } + +impl TlvEncode for T +where + T: AsRef<[(u8, Vec)]>, +{ + fn encode(&self) -> Vec { + self.as_ref() + .iter() + .flat_map(|(tlv_type, tlv_data)| { + let length = tlv_data.len() as u32; + let packets = tlv_data + .chunks(255) + .enumerate() + .flat_map(|(packet_num, data)| { + let mut packet = vec![*tlv_type]; + packet.push((length - 255 * (packet_num as u32)) as u8); + packet.extend_from_slice(data); + packet + }) + .collect::>(); + if packets.is_empty() { + let mut first_packet = vec![*tlv_type]; + first_packet.extend_from_slice(&length.to_le_bytes()); + first_packet.push(0); + first_packet + } else { + packets + } + }) + .collect() + } +} + +#[derive(Debug, Clone)] +pub struct Tlv(u8, TlvData); + +impl Tlv { + pub fn new>(tlv_type: A, tlv_data: TlvData) -> Self { + Self(tlv_type.into(), tlv_data) + } + + pub fn encode(&self) -> Vec { + let Tlv(tlv_type, data) = self; + let bytes = match data { + TlvData::Bytes(v) => v.clone(), + TlvData::Tlv8(v) => todo!(), + TlvData::Integer(v) => v.to_le_bytes(), + TlvData::Float(v) => v.to_le_bytes().to_vec(), + TlvData::String(v) => v.as_bytes().to_vec(), + TlvData::Uint(v) => v.to_le_bytes(), + }; + let length = bytes.len() as u32; + let packets = bytes + .chunks(255) + .enumerate() + .flat_map(|(packet_num, data)| { + let mut packet = vec![*tlv_type]; + packet.push((length - 255 * (packet_num as u32)) as u8); + packet.extend_from_slice(data); + packet + }) + .collect::>(); + if packets.is_empty() { + let mut first_packet = vec![*tlv_type]; + first_packet.extend_from_slice(&length.to_le_bytes()); + first_packet.push(0); + first_packet + } else { + packets + } + } + + pub fn decode_single(data: &[u8]) -> Result { + let tlv_type = data[0]; + let tlv_len = data[1]; + let data = &data[2..]; + Ok(Self( + tlv_type, + TlvData::Bytes(if tlv_len == 0 { vec![] } else { data.to_vec() }), + )) + } + + pub fn tlv_type(&self) -> u8 { + self.0 + } + + pub fn tlv_data(&self) -> &TlvData { + &self.1 + } + + pub fn is_type>(&self, value: T) -> bool { + self.0 == value.into() + } +} + +impl TlvData { + pub fn as_bytes(&self) -> Vec { + match self { + TlvData::Bytes(val) => val.clone(), + TlvData::Tlv8(_val) => todo!(), + TlvData::Integer(val) => val.to_le_bytes(), + TlvData::Float(_val) => todo!(), + TlvData::String(val) => val.as_bytes().to_vec(), + TlvData::Uint(val) => val.to_le_bytes(), + } + } +} + +pub trait TlvEncodableData { + fn encode_value(&self) -> Vec; +} + +impl TlvEncodableData for &[u8; N] { + fn encode_value(&self) -> Vec { + (*self as &[u8]).encode_value() + } +} + +impl TlvEncodableData for &[u8] { + fn encode_value(&self) -> Vec { + self.to_vec() + } +} + +impl TlvEncodableData for i8 { + fn encode_value(&self) -> Vec { + self.to_le_bytes().to_vec() + } +} + +impl TlvEncodableData for i16 { + fn encode_value(&self) -> Vec { + self.to_le_bytes().to_vec() + } +} + +impl TlvEncodableData for i32 { + fn encode_value(&self) -> Vec { + self.to_le_bytes().to_vec() + } +} + +impl TlvEncodableData for i64 { + fn encode_value(&self) -> Vec { + self.to_le_bytes().to_vec() + } +} + +impl TlvEncodableData for isize { + fn encode_value(&self) -> Vec { + self.to_le_bytes().to_vec() + } +} + +impl TlvEncodableData for u8 { + fn encode_value(&self) -> Vec { + self.to_le_bytes().to_vec() + } +} + +impl TlvEncodableData for u16 { + fn encode_value(&self) -> Vec { + self.to_le_bytes().to_vec() + } +} + +impl TlvEncodableData for u32 { + fn encode_value(&self) -> Vec { + self.to_le_bytes().to_vec() + } +} + +impl TlvEncodableData for u64 { + fn encode_value(&self) -> Vec { + self.to_le_bytes().to_vec() + } +} + +impl TlvEncodableData for usize { + fn encode_value(&self) -> Vec { + self.to_le_bytes().to_vec() + } +} + +impl TlvEncodableData for f32 { + fn encode_value(&self) -> Vec { + self.to_le_bytes().to_vec() + } +} + +impl TlvEncodableData for f64 { + fn encode_value(&self) -> Vec { + self.to_le_bytes().to_vec() + } +} + +impl TlvEncodableData for String { + fn encode_value(&self) -> Vec { + self.as_bytes().to_vec() + } +}