//! Library which implements serialization and deserialization for the packet format used by the //! base station and all sensor/display nodes. #![no_std] #[cfg(test)] extern crate std; use core::convert::TryInto; use crc16::{State, KERMIT}; use xxtea_nostd::{decrypt, encrypt}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Packet { GetSalt, Salt(u64), Report(Report), GetValues(GetValues), Values(Values), } impl Packet { pub fn decrypt_and_decode(key: &[u8], data: &mut [u8]) -> Result { decrypt_cbc(key, data); let checksummed = &data[8..]; let remainder = State::::calculate(&checksummed); if remainder != 0 { return Err(Error::CRC); } Self::decode(&checksummed[..22]) } pub fn encode_and_encrypt(&self, key: &[u8], salt: u64, data: &mut [u8]) -> bool { data[0..8].copy_from_slice(&salt.to_le_bytes()); if !self.encode(&mut data[8..30]) { return false; } let checksum = State::::calculate(&data[8..30]); data[30..].copy_from_slice(&checksum.to_le_bytes()); encrypt_cbc(key, data); true } fn decode(data: &[u8]) -> Result { 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 => Ok(Self::GetSalt), 1 => Ok(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 => Ok(Self::Report(Report::decode(count, &data[1..])?)), 3 => Ok(Self::GetValues(GetValues::decode(count, &data[1..])?)), 4 => Ok(Self::Values(Values::decode(count, &data[1..])?)), _ => Err(Error::InvalidPacketType), } } fn encode(&self, data: &mut [u8]) -> bool { match self { Self::GetSalt => { data[0] = 0; true } Self::Salt(salt) => { data[0] = 1; data[1..8].copy_from_slice(&(salt >> 8 as u64).to_le_bytes()[0..7]); true } Self::Report(report) => { data[0] = (report.count << 5) | 2; report.encode(&mut data[1..]) } Self::GetValues(get_values) => { data[0] = (get_values.count << 5) | 2; get_values.encode(&mut data[1..]) } Self::Values(values) => { data[0] = (values.count << 5) | 2; values.encode(&mut data[1..]) } } } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Report { pub count: u8, pub values: [Value; 8], } impl Report { fn decode(count: u8, mut data: &[u8]) -> Result { let mut report = Self { count, values: [Value::Invalid; 8], }; for i in 0..count { report.values[i as usize] = Value::decode(&mut data)?; } Ok(report) } fn encode(&self, mut data: &mut [u8]) -> bool { for i in 0..self.count { let value_len = self.values[i as usize].encoded_length(); if data.len() < value_len { return false; } if !self.values[i as usize].encode(data) { return false; } data = &mut data[value_len..]; } true } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct GetValues { pub count: u8, pub location: Location, pub types_: [ValueType; 8], } impl GetValues { fn decode(_count: u8, _data: &[u8]) -> Result { // TODO Err(Error::InvalidPacketType) } fn encode(&self, mut _data: &[u8]) -> bool { // TODO false } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Values { pub count: u8, pub location: Location, pub values: [Value; 8], } impl Values { fn decode(_count: u8, _data: &[u8]) -> Result { // TODO Err(Error::InvalidPacketType) } fn encode(&self, mut _data: &[u8]) -> bool { // TODO false } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Location { Livingroom, Bathroom, Bedroom, Kitchen, Balcony, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Value { Invalid, Time(u64), Temperature(i16), Pressure(u32), Humidity(u16), } impl Value { fn decode(data: &mut &[u8]) -> Result { let type_ = data[0]; let length = match type_ { 0 => 9, 1 => 3, 2 => 5, 3 => 3, _ => return Err(Error::InvalidValueType), }; if data.len() < length { return Err(Error::TooShort); } 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 Err(Error::InvalidValueType), }; *data = &data[length..]; Ok(result) } fn encode(&self, data: &mut [u8]) -> bool { match self { Self::Invalid => {} Self::Time(time) => { data[0] = 0; data[1..9].copy_from_slice(&time.to_le_bytes()); } Self::Temperature(temperature) => { data[0] = 1; data[1..3].copy_from_slice(&temperature.to_le_bytes()); } Self::Pressure(pressure) => { data[0] = 2; data[1..5].copy_from_slice(&pressure.to_le_bytes()); } Self::Humidity(humidity) => { data[0] = 3; data[1..3].copy_from_slice(&humidity.to_le_bytes()); } }; true } fn encoded_length(&self) -> usize { match self { Self::Invalid => 0, Self::Time(_) => 9, Self::Temperature(_) => 3, Self::Pressure(_) => 5, Self::Humidity(_) => 3, } } pub fn split(&self) -> Option<(ValueType, i64)> { match self { Self::Invalid => None, Self::Time(t) => Some((ValueType::Time, *t as i64)), Self::Temperature(t) => Some((ValueType::Temperature, *t as i64)), Self::Pressure(p) => Some((ValueType::Pressure, *p as i64)), Self::Humidity(h) => Some((ValueType::Humidity, *h as i64)), } } pub fn combine(type_: ValueType, value: i64) -> Value { match type_ { ValueType::Time => Self::Time(value as u64), ValueType::Temperature => Self::Temperature(value as i16), ValueType::Pressure => Self::Pressure(value as u32), ValueType::Humidity => Self::Humidity(value as u16), } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ValueType { Time, Temperature, Pressure, Humidity, } fn encrypt_cbc(key: &[u8], data: &mut [u8]) { for i in 0..data.len() / 8 - 1 { let (prev, block) = data[i * 8..i * 8 + 16].split_at_mut(8); for j in 0..8 { block[j] ^= prev[j]; } encrypt(&key, block); } } 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]; } } } #[derive(Debug, Copy, Clone)] pub enum Error { CRC, InvalidPacketType, InvalidValueType, InvalidLocation, TooShort, } #[cfg(test)] mod tests { use super::*; use std::println; #[test] fn test_identity() { let packets = [Packet::GetSalt, Packet::Salt(0x12345678abcdef12)]; let key = [ 0x12, 0x34, 0x56, 0x78, 0xab, 0xcd, 0xef, 0x12, 0x12, 0x34, 0x56, 0x78, 0xab, 0xcd, 0xef, 0x12, ]; for packet in packets.iter() { let mut encoded = [0u8; 32]; packet.encode_and_encrypt(&key, 0x1234123412341234, &mut encoded); assert_eq!(encoded[0], 0x34); assert_eq!(encoded[7], 0x12); let decoded = Packet::decrypt_and_decode(&key, &mut encoded).unwrap(); // TODO: Compare encoded and decoded. } } }