//! Library which implements serialization and deserialization for the packet format used by the //! base station and all sensor/display nodes. #![no_std] use core::convert::TryInto; use crc16::{State, KERMIT}; use xxtea_nostd::decrypt; #[derive(Debug, Clone)] pub enum Packet { GetSalt, Salt(u64), Report(Report), GetValues(GetValues), Values(Values), } impl Packet { pub fn decrypt_and_parse(key: &[u8], data: &mut [u8]) -> Option { decrypt_cbc(key, data); let checksummed = &data[8..]; let remainder = State::::calculate(&checksummed); if remainder != 0 { return None; } Self::decode(&checksummed[..22]) } fn decode(data: &[u8]) -> Option { let type_ = data[0] & 0x1f; // count can be at most 7, so we do not need any checks when indexing 8-element arrays // below. let count = data[0] >> 5; match type_ { 0 => Some(Self::GetSalt), 1 => Some(Self::Salt({ // The lowest 8 bit of the salt are the device ID and are filled in by the caller. u64::from_le_bytes(data[1..9].try_into().unwrap()) << 8 })), 2 => Some(Self::Report(Report::decode(count, &data[1..])?)), 3 => Some(Self::GetValues(GetValues::decode(count, &data[1..])?)), 4 => Some(Self::Values(Values::decode(count, &data[1..])?)), _ => None, } } } #[derive(Debug, Clone)] pub struct Report { pub count: u8, pub values: [Value; 8], } impl Report { fn decode(count: u8, mut data: &[u8]) -> Option { let mut report = Self { count, values: [Value::Invalid; 8], }; for i in 0..count { report.values[i as usize] = Value::decode(&mut data)?; } Some(report) } } #[derive(Debug, Clone)] pub struct GetValues { pub count: u8, pub location: Location, pub types_: [ValueType; 8], } impl GetValues { fn decode(_count: u8, _data: &[u8]) -> Option { // TODO None } } #[derive(Debug, Clone)] pub struct Values { pub count: u8, pub location: Location, pub values: [Value; 8], } impl Values { fn decode(_count: u8, _data: &[u8]) -> Option { // TODO None } } #[derive(Debug, Clone, Copy)] pub enum Location { Livingroom, Bathroom, Bedroom, Kitchen, Balcony, } #[derive(Debug, Clone, Copy)] pub enum Value { Invalid, Time(u64), Temperature(i16), Pressure(u32), Humidity(u16), } impl Value { fn decode(data: &mut &[u8]) -> Option { let type_ = data[0]; let length = match type_ { 0 => 9, 1 => 3, 2 => 5, 3 => 3, _ => return None, }; if data.len() < length { return None; } let result = match type_ { 0 => Self::Time(u64::from_le_bytes(data[1..9].try_into().unwrap())), 1 => Self::Temperature(i16::from_le_bytes(data[1..3].try_into().unwrap())), 2 => Self::Pressure(u32::from_le_bytes(data[1..5].try_into().unwrap())), 3 => Self::Humidity(u16::from_le_bytes(data[1..3].try_into().unwrap())), _ => return None, }; *data = &data[length..]; Some(result) } } #[derive(Debug, Clone, Copy)] pub enum ValueType { Time, Temperature, Pressure, Humidity, } fn decrypt_cbc(key: &[u8], data: &mut [u8]) { let mut blocks = data.rchunks_mut(8).peekable(); loop { let block = blocks.next().unwrap(); let prev_block = match blocks.peek() { Some(b) => b, None => break, }; decrypt(&key, block); for i in 0..8 { block[i] ^= prev_block[i]; } } }