Skip to main content

pamoja_lorawan/relay/
trusted.rs

1//! The end devices a relay verifies wake-on-radio frames for, TS011-1.0.1 sections 10.4
2//! and 10.5.
3
4use super::keys::WorKeys;
5use super::limits::TokenBucket;
6use super::wor::{Carrier, SealedWor};
7use super::TRUSTED_ED_NUMBER;
8use crate::LorawanError;
9
10/// An end device a relay trusts: its keys, the WOR frame counter it expects next, and how
11/// much of it the relay forwards.
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub struct TrustedDevice {
14    dev_addr: u32,
15    keys: WorKeys,
16    next_wfcnt: u64,
17    bucket: TokenBucket,
18}
19
20impl TrustedDevice {
21    /// Trusts an end device, as `UpdateUplinkListReq` asks.
22    ///
23    /// # Arguments
24    ///
25    /// * `dev_addr` - its address.
26    /// * `root_wor_s_key` - the root relay session key the network sent for it, which its
27    ///   integrity and encryption keys derive from.
28    /// * `next_wfcnt` - the WOR frame counter the network expects from it next.
29    /// * `bucket` - how many of its uplinks the relay forwards.
30    ///
31    /// # Returns
32    ///
33    /// The trusted device.
34    #[must_use]
35    pub fn new(
36        dev_addr: u32,
37        root_wor_s_key: &[u8; 16],
38        next_wfcnt: u32,
39        bucket: TokenBucket,
40    ) -> TrustedDevice {
41        TrustedDevice::with_keys(
42            dev_addr,
43            WorKeys::derive(root_wor_s_key, dev_addr),
44            next_wfcnt,
45            bucket,
46        )
47    }
48
49    /// Trusts an end device whose keys are already derived.
50    ///
51    /// # Arguments
52    ///
53    /// * `dev_addr` - its address.
54    /// * `keys` - its integrity and encryption keys.
55    /// * `next_wfcnt` - the WOR frame counter expected from it next.
56    /// * `bucket` - how many of its uplinks the relay forwards.
57    ///
58    /// # Returns
59    ///
60    /// The trusted device.
61    #[must_use]
62    pub const fn with_keys(
63        dev_addr: u32,
64        keys: WorKeys,
65        next_wfcnt: u32,
66        bucket: TokenBucket,
67    ) -> TrustedDevice {
68        TrustedDevice {
69            dev_addr,
70            keys,
71            next_wfcnt: next_wfcnt as u64,
72            bucket,
73        }
74    }
75
76    /// Returns the device's address.
77    ///
78    /// # Returns
79    ///
80    /// The address.
81    #[must_use]
82    pub const fn dev_addr(&self) -> u32 {
83        self.dev_addr
84    }
85
86    /// Returns the device's keys.
87    ///
88    /// # Returns
89    ///
90    /// The integrity and encryption keys.
91    #[must_use]
92    pub const fn keys(&self) -> &WorKeys {
93        &self.keys
94    }
95
96    /// Returns the WOR frame counter the relay accepts next, at the least.
97    ///
98    /// # Returns
99    ///
100    /// The counter, or `None` once a frame with the last counter there is has been
101    /// accepted.
102    #[must_use]
103    pub const fn next_wfcnt(&self) -> Option<u32> {
104        if self.next_wfcnt > u32::MAX as u64 {
105            None
106        } else {
107            Some(self.next_wfcnt as u32)
108        }
109    }
110
111    /// Returns the last WOR frame counter the relay accepted, which `CtrlUplinkListAns`
112    /// reports.
113    ///
114    /// # Returns
115    ///
116    /// The counter. Before any frame arrives, it is one less than the counter the network
117    /// said to expect, wrapping, so the network reads back what it set.
118    #[must_use]
119    pub const fn last_wfcnt(&self) -> u32 {
120        (self.next_wfcnt as u32).wrapping_sub(1)
121    }
122
123    /// Returns how many of the device's uplinks the relay forwards.
124    ///
125    /// # Returns
126    ///
127    /// The bucket.
128    #[must_use]
129    pub const fn bucket(&self) -> &TokenBucket {
130        &self.bucket
131    }
132
133    /// Returns the device's bucket to spend from or reconfigure.
134    ///
135    /// # Returns
136    ///
137    /// The bucket.
138    pub fn bucket_mut(&mut self) -> &mut TokenBucket {
139        &mut self.bucket
140    }
141
142    /// Checks and opens a wake-on-radio frame from the device, then moves the counter past
143    /// it.
144    ///
145    /// The frame carries the counter's low sixteen bits. The relay takes the first full
146    /// counter at or after the one it expects with those bits, so a frame it has seen, or
147    /// an older one, does not verify.
148    ///
149    /// # Arguments
150    ///
151    /// * `wor` - the frame.
152    /// * `carrier` - the carrier it arrived on.
153    ///
154    /// # Returns
155    ///
156    /// The full counter the frame carried and where its uplink follows.
157    ///
158    /// # Errors
159    ///
160    /// Returns [`LorawanError::FcntMismatch`] for a frame addressed elsewhere or past the
161    /// last counter, and [`LorawanError::MicMismatch`] when it does not verify, leaving the
162    /// counter where it was.
163    pub fn open(
164        &mut self,
165        wor: &SealedWor,
166        carrier: Carrier,
167    ) -> Result<(u32, Carrier), LorawanError> {
168        if wor.dev_addr() != self.dev_addr {
169            return Err(LorawanError::FcntMismatch);
170        }
171        let mut candidate = (self.next_wfcnt & !0xFFFF) | u64::from(wor.wfcnt());
172        if candidate < self.next_wfcnt {
173            candidate += 0x1_0000;
174        }
175        let wfcnt = u32::try_from(candidate).map_err(|_| LorawanError::FcntMismatch)?;
176        let uplink = wor.open(&self.keys, wfcnt, carrier)?;
177        self.next_wfcnt = candidate + 1;
178        Ok((wfcnt, uplink))
179    }
180}
181
182/// The end devices a relay trusts, [`TRUSTED_ED_NUMBER`] of them, each at the index the
183/// network gave it.
184#[derive(Clone, Copy, Debug, PartialEq, Eq)]
185pub struct TrustedDevices {
186    entries: [Option<TrustedDevice>; TRUSTED_ED_NUMBER],
187}
188
189impl TrustedDevices {
190    /// An empty list.
191    ///
192    /// # Returns
193    ///
194    /// The list.
195    #[must_use]
196    pub const fn new() -> TrustedDevices {
197        TrustedDevices {
198            entries: [None; TRUSTED_ED_NUMBER],
199        }
200    }
201
202    /// Returns the device at an index.
203    ///
204    /// # Arguments
205    ///
206    /// * `index` - the entry, 0 to 15.
207    ///
208    /// # Returns
209    ///
210    /// The device, or `None` for an empty entry or one out of range.
211    #[must_use]
212    pub fn get(&self, index: u8) -> Option<&TrustedDevice> {
213        self.entries.get(usize::from(index))?.as_ref()
214    }
215
216    /// Returns the device at an index to change.
217    ///
218    /// # Arguments
219    ///
220    /// * `index` - the entry, 0 to 15.
221    ///
222    /// # Returns
223    ///
224    /// The device, or `None` for an empty entry or one out of range.
225    pub fn get_mut(&mut self, index: u8) -> Option<&mut TrustedDevice> {
226        self.entries.get_mut(usize::from(index))?.as_mut()
227    }
228
229    /// Puts a device at an index, replacing whatever was there.
230    ///
231    /// # Arguments
232    ///
233    /// * `index` - the entry, 0 to 15.
234    /// * `device` - the device.
235    ///
236    /// # Returns
237    ///
238    /// `false` for an index out of range.
239    pub fn set(&mut self, index: u8, device: TrustedDevice) -> bool {
240        match self.entries.get_mut(usize::from(index)) {
241            Some(entry) => {
242                *entry = Some(device);
243                true
244            }
245            None => false,
246        }
247    }
248
249    /// Stops trusting the device at an index.
250    ///
251    /// # Arguments
252    ///
253    /// * `index` - the entry, 0 to 15.
254    ///
255    /// # Returns
256    ///
257    /// The device that was there.
258    pub fn remove(&mut self, index: u8) -> Option<TrustedDevice> {
259        self.entries.get_mut(usize::from(index))?.take()
260    }
261
262    /// Lists the devices.
263    ///
264    /// # Returns
265    ///
266    /// Each device with its index, in index order.
267    pub fn iter(&self) -> impl Iterator<Item = (u8, &TrustedDevice)> + '_ {
268        (0u8..)
269            .zip(self.entries.iter())
270            .filter_map(|(index, entry)| entry.as_ref().map(|device| (index, device)))
271    }
272
273    /// Reports whether any device has an address.
274    ///
275    /// # Arguments
276    ///
277    /// * `dev_addr` - the address.
278    ///
279    /// # Returns
280    ///
281    /// `true` when a trusted device has it.
282    #[must_use]
283    pub fn knows(&self, dev_addr: u32) -> bool {
284        self.iter().any(|(_, device)| device.dev_addr() == dev_addr)
285    }
286
287    /// Checks and opens a wake-on-radio frame with the keys of whichever device it verifies
288    /// for.
289    ///
290    /// # Arguments
291    ///
292    /// * `wor` - the frame.
293    /// * `carrier` - the carrier it arrived on.
294    ///
295    /// # Returns
296    ///
297    /// The index of the device it came from, the full counter it carried, and where its
298    /// uplink follows; or `None` when no trusted device has the address it names.
299    ///
300    /// # Errors
301    ///
302    /// When devices have the address but the frame verifies for none of them, the last of
303    /// their errors, as [`TrustedDevice::open`] gives them.
304    pub fn open(
305        &mut self,
306        wor: &SealedWor,
307        carrier: Carrier,
308    ) -> Option<Result<(u8, u32, Carrier), LorawanError>> {
309        let mut outcome = None;
310        for (index, entry) in (0u8..).zip(self.entries.iter_mut()) {
311            let Some(device) = entry
312                .as_mut()
313                .filter(|device| device.dev_addr == wor.dev_addr())
314            else {
315                continue;
316            };
317            match device.open(wor, carrier) {
318                Ok((wfcnt, uplink)) => return Some(Ok((index, wfcnt, uplink))),
319                Err(error) => outcome = Some(Err(error)),
320            }
321        }
322        outcome
323    }
324
325    /// Adds what every device's bucket earns over some hours.
326    ///
327    /// # Arguments
328    ///
329    /// * `hours` - the whole hours since the last reload.
330    pub fn reload(&mut self, hours: u64) {
331        for device in self.entries.iter_mut().flatten() {
332            device.bucket.reload(hours);
333        }
334    }
335}
336
337impl Default for TrustedDevices {
338    fn default() -> TrustedDevices {
339        TrustedDevices::new()
340    }
341}