| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- //! 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)]
- 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<Packet, Error> {
- decrypt_cbc(key, data);
- let checksummed = &data[8..];
- let remainder = State::<KERMIT>::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::<KERMIT>::calculate(&data[8..30]);
- data[30..].copy_from_slice(&checksum.to_le_bytes());
- encrypt_cbc(key, data);
- true
- }
-
- fn decode(data: &[u8]) -> Result<Packet, Error> {
- 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)]
- pub struct Report {
- pub count: u8,
- pub values: [Value; 8],
- }
-
- impl Report {
- fn decode(count: u8, mut data: &[u8]) -> Result<Report, Error> {
- 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)]
- pub struct GetValues {
- pub count: u8,
- pub location: Location,
- pub types_: [ValueType; 8],
- }
-
- impl GetValues {
- fn decode(_count: u8, _data: &[u8]) -> Result<GetValues, Error> {
- // TODO
- Err(Error::InvalidPacketType)
- }
-
- fn encode(&self, mut _data: &[u8]) -> bool {
- // TODO
- false
- }
- }
-
- #[derive(Debug, Clone, PartialEq)]
- pub struct Values {
- pub count: u8,
- pub location: Location,
- pub values: [Value; 8],
- }
-
- impl Values {
- fn decode(_count: u8, _data: &[u8]) -> Result<Values, Error> {
- // TODO
- Err(Error::InvalidPacketType)
- }
-
- fn encode(&self, mut _data: &[u8]) -> bool {
- // TODO
- false
- }
- }
-
- #[derive(Debug, Clone, Copy, PartialEq)]
- pub enum Location {
- Livingroom,
- Bathroom,
- Bedroom,
- Kitchen,
- Balcony,
- }
-
- #[derive(Debug, Clone, Copy, PartialEq)]
- pub enum Value {
- Invalid,
- Time(u64),
- Temperature(i16),
- Pressure(u32),
- Humidity(u16),
- }
-
- impl Value {
- fn decode(data: &mut &[u8]) -> Result<Value, Error> {
- 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)]
- 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.
- }
- }
- }
|