| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- //! 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<Packet> {
- decrypt_cbc(key, data);
- let checksummed = &data[8..];
- let remainder = State::<KERMIT>::calculate(&checksummed);
- if remainder != 0 {
- return None;
- }
- Self::decode(&checksummed[..22])
- }
-
- fn decode(data: &[u8]) -> Option<Packet> {
- 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<Report> {
- 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<GetValues> {
- // 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<Values> {
- // 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<Value> {
- 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];
- }
- }
- }
|