Parcourir la source

Add a library for packet encoding/decoding in Rust.

Mathias Gottschlag il y a 5 ans
Parent
révision
50c643ebfa
2 fichiers modifiés avec 169 ajouts et 0 suppressions
  1. 9
    0
      common/rust-protocol/Cargo.toml
  2. 160
    0
      common/rust-protocol/src/lib.rs

+ 9
- 0
common/rust-protocol/Cargo.toml Voir le fichier

@@ -0,0 +1,9 @@
1
+[package]
2
+name = "protocol"
3
+version = "0.1.0"
4
+authors = ["Mathias Gottschlag <mgottschlag@gmail.com>"]
5
+edition = "2018"
6
+
7
+[dependencies]
8
+crc16 = "0.4.0"
9
+xxtea-nostd = "0.1.0"

+ 160
- 0
common/rust-protocol/src/lib.rs Voir le fichier

@@ -0,0 +1,160 @@
1
+//! Library which implements serialization and deserialization for the packet format used by the
2
+//! base station and all sensor/display nodes.
3
+#![no_std]
4
+
5
+use core::convert::TryInto;
6
+
7
+use crc16::{State, KERMIT};
8
+use xxtea_nostd::decrypt;
9
+
10
+#[derive(Clone)]
11
+pub enum Packet {
12
+    Salt(u64),
13
+    Report(Report),
14
+    GetValues(GetValues),
15
+    Values(Values),
16
+}
17
+
18
+impl Packet {
19
+    pub fn decrypt_and_parse(key: &[u8], data: &mut [u8]) -> Option<Packet> {
20
+        decrypt_cbc(key, data);
21
+        let checksummed = &data[8..];
22
+        let calculated_crc = State::<KERMIT>::calculate(&checksummed);
23
+        let crc = u16::from_le_bytes(data[30..].try_into().unwrap());
24
+        if crc != calculated_crc {
25
+            return None;
26
+        }
27
+        Self::decode(&checksummed[..22])
28
+    }
29
+
30
+    fn decode(data: &[u8]) -> Option<Packet> {
31
+        let type_ = data[0] & 0x1f;
32
+        // count can be at most 7, so we do not need any checks when indexing 8-element arrays
33
+        // below.
34
+        let count = data[0] >> 5;
35
+        match type_ {
36
+            0 => Some(Self::Salt({
37
+                // The lowest 8 bit of the salt are the device ID and are filled in by the caller.
38
+                u64::from_le_bytes(data[1..9].try_into().unwrap()) << 8
39
+            })),
40
+            1 => Some(Self::Report(Report::decode(count, &data[1..])?)),
41
+            2 => Some(Self::GetValues(GetValues::decode(count, &data[1..])?)),
42
+            3 => Some(Self::Values(Values::decode(count, &data[1..])?)),
43
+            _ => None,
44
+        }
45
+    }
46
+}
47
+
48
+#[derive(Clone)]
49
+pub struct Report {
50
+    count: u8,
51
+    values: [Value; 8],
52
+}
53
+
54
+impl Report {
55
+    fn decode(count: u8, mut data: &[u8]) -> Option<Report> {
56
+        let mut report = Self {
57
+            count,
58
+            values: [Value::Invalid; 8],
59
+        };
60
+        for i in 0..count {
61
+            report.values[i as usize] = Value::decode(&mut data)?;
62
+        }
63
+        None
64
+    }
65
+}
66
+
67
+#[derive(Clone)]
68
+pub struct GetValues {
69
+    count: u8,
70
+    location: Location,
71
+    types_: [ValueType; 8],
72
+}
73
+
74
+impl GetValues {
75
+    fn decode(_count: u8, _data: &[u8]) -> Option<GetValues> {
76
+        // TODO
77
+        None
78
+    }
79
+}
80
+
81
+#[derive(Clone)]
82
+pub struct Values {
83
+    count: u8,
84
+    location: Location,
85
+    values: [Value; 8],
86
+}
87
+
88
+impl Values {
89
+    fn decode(_count: u8, _data: &[u8]) -> Option<Values> {
90
+        // TODO
91
+        None
92
+    }
93
+}
94
+
95
+#[derive(Clone, Copy)]
96
+pub enum Location {
97
+    Livingroom,
98
+    Bathroom,
99
+    Bedroom,
100
+    Kitchen,
101
+    Balcony,
102
+}
103
+
104
+#[derive(Clone, Copy)]
105
+pub enum Value {
106
+    Invalid,
107
+    Time(u64),
108
+    Temperature(i16),
109
+    Pressure(u32),
110
+    Humidity(u16),
111
+}
112
+
113
+impl Value {
114
+    fn decode(data: &mut &[u8]) -> Option<Value> {
115
+        let type_ = data[0];
116
+        let length = match type_ {
117
+            0 => 9,
118
+            1 => 3,
119
+            2 => 5,
120
+            3 => 3,
121
+            _ => return None,
122
+        };
123
+        if data.len() < length {
124
+            return None;
125
+        }
126
+        let result = match type_ {
127
+            0 => Self::Time(u64::from_le_bytes(data[1..9].try_into().unwrap())),
128
+            1 => Self::Temperature(i16::from_le_bytes(data[1..3].try_into().unwrap())),
129
+            2 => Self::Pressure(u32::from_le_bytes(data[1..5].try_into().unwrap())),
130
+            3 => Self::Humidity(u16::from_le_bytes(data[1..3].try_into().unwrap())),
131
+            _ => return None,
132
+        };
133
+        *data = &data[length..];
134
+        Some(result)
135
+    }
136
+}
137
+
138
+#[derive(Clone, Copy)]
139
+pub enum ValueType {
140
+    Time,
141
+    Temperature,
142
+    Pressure,
143
+    Humidity,
144
+}
145
+
146
+fn decrypt_cbc(key: &[u8], data: &mut [u8]) {
147
+    let mut blocks = data.rchunks_mut(8).peekable();
148
+    loop {
149
+        let block = blocks.next().unwrap();
150
+        let prev_block = match blocks.peek() {
151
+            Some(b) => b,
152
+            None => break,
153
+        };
154
+        decrypt(&key, block);
155
+
156
+        for i in 0..8 {
157
+            block[i] ^= prev_block[i];
158
+        }
159
+    }
160
+}

Loading…
Annuler
Enregistrer