lorawan_device/mac/
session.rs

1use crate::{region, AppSKey, Downlink, NewSKey};
2use heapless::Vec;
3use lorawan::keys::CryptoFactory;
4use lorawan::maccommands::{DownlinkMacCommand, MacCommandIterator};
5use lorawan::{
6    creator::DataPayloadCreator,
7    maccommands::SerializableMacCommand,
8    parser::{parse_with_factory as lorawan_parse, *},
9    parser::{DecryptedJoinAcceptPayload, DevAddr},
10};
11
12use generic_array::{typenum::U256, GenericArray};
13
14use crate::radio::RadioBuffer;
15
16use super::{
17    otaa::{DevNonce, NetworkCredentials},
18    uplink, FcntUp, Response, SendData,
19};
20
21#[derive(Clone, Debug)]
22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub struct Session {
25    pub uplink: uplink::Uplink,
26    pub confirmed: bool,
27    pub newskey: NewSKey,
28    pub appskey: AppSKey,
29    pub devaddr: DevAddr<[u8; 4]>,
30    pub fcnt_up: u32,
31    pub fcnt_down: u32,
32}
33
34#[derive(Clone, Debug)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36pub struct SessionKeys {
37    pub newskey: NewSKey,
38    pub appskey: AppSKey,
39    pub devaddr: DevAddr<[u8; 4]>,
40}
41
42impl From<Session> for SessionKeys {
43    fn from(session: Session) -> Self {
44        Self { newskey: session.newskey, appskey: session.appskey, devaddr: session.devaddr }
45    }
46}
47
48impl Session {
49    pub fn derive_new<T: AsRef<[u8]>, F: CryptoFactory>(
50        decrypt: &DecryptedJoinAcceptPayload<T, F>,
51        devnonce: DevNonce,
52        credentials: &NetworkCredentials,
53    ) -> Self {
54        Self::new(
55            decrypt.derive_newskey(&devnonce, credentials.appkey()),
56            decrypt.derive_appskey(&devnonce, credentials.appkey()),
57            DevAddr::new([
58                decrypt.dev_addr().as_ref()[0],
59                decrypt.dev_addr().as_ref()[1],
60                decrypt.dev_addr().as_ref()[2],
61                decrypt.dev_addr().as_ref()[3],
62            ])
63            .unwrap(),
64        )
65    }
66
67    pub fn new(newskey: NewSKey, appskey: AppSKey, devaddr: DevAddr<[u8; 4]>) -> Self {
68        Self {
69            newskey,
70            appskey,
71            devaddr,
72            confirmed: false,
73            fcnt_down: 0,
74            fcnt_up: 0,
75            uplink: uplink::Uplink::default(),
76        }
77    }
78
79    pub fn devaddr(&self) -> &DevAddr<[u8; 4]> {
80        &self.devaddr
81    }
82    pub fn appskey(&self) -> &AppSKey {
83        &self.appskey
84    }
85    pub fn newskey(&self) -> &NewSKey {
86        &self.newskey
87    }
88
89    pub fn get_session_keys(&self) -> Option<SessionKeys> {
90        Some(SessionKeys { newskey: self.newskey, appskey: self.appskey, devaddr: self.devaddr })
91    }
92}
93
94impl Session {
95    pub(crate) fn handle_rx<C: CryptoFactory + Default, const N: usize, const D: usize>(
96        &mut self,
97        region: &mut region::Configuration,
98        configuration: &mut super::Configuration,
99        rx: &mut RadioBuffer<N>,
100        dl: &mut Vec<Downlink, D>,
101        ignore_mac: bool,
102    ) -> Response {
103        if let Ok(PhyPayload::Data(DataPayload::Encrypted(encrypted_data))) =
104            lorawan_parse(rx.as_mut_for_read(), C::default())
105        {
106            if self.devaddr() == &encrypted_data.fhdr().dev_addr() {
107                let fcnt = encrypted_data.fhdr().fcnt() as u32;
108                let confirmed = encrypted_data.is_confirmed();
109                if encrypted_data.validate_mic(self.newskey().inner(), fcnt)
110                    && (fcnt > self.fcnt_down || fcnt == 0)
111                {
112                    self.fcnt_down = fcnt;
113                    // We can safely unwrap here because we already validated the MIC
114                    let decrypted = encrypted_data
115                        .decrypt(
116                            Some(self.newskey().inner()),
117                            Some(self.appskey().inner()),
118                            self.fcnt_down,
119                        )
120                        .unwrap();
121
122                    if !ignore_mac {
123                        // MAC commands may be in the FHDR or the FRMPayload
124                        configuration.handle_downlink_macs(
125                            region,
126                            &mut self.uplink,
127                            MacCommandIterator::<DownlinkMacCommand>::new(decrypted.fhdr().data()),
128                        );
129                        if let FRMPayload::MACCommands(mac_cmds) = decrypted.frm_payload() {
130                            configuration.handle_downlink_macs(
131                                region,
132                                &mut self.uplink,
133                                MacCommandIterator::<DownlinkMacCommand>::new(mac_cmds.data()),
134                            );
135                        }
136                    }
137
138                    if confirmed {
139                        self.uplink.set_downlink_confirmation();
140                    }
141
142                    return if self.fcnt_up == 0xFFFF_FFFF {
143                        // if the FCnt is used up, the session has expired
144                        Response::SessionExpired
145                    } else {
146                        // we can always increment fcnt_up when we receive a downlink
147                        self.fcnt_up += 1;
148                        if let (Some(fport), FRMPayload::Data(data)) =
149                            (decrypted.f_port(), decrypted.frm_payload())
150                        {
151                            // heapless Vec from slice fails only if slice is too large.
152                            // A data FRM payload will never exceed 256 bytes.
153                            let data = Vec::from_slice(data).unwrap();
154                            // TODO: propagate error type when heapless vec is full?
155                            let _ = dl.push(Downlink { data, fport });
156                        }
157                        Response::DownlinkReceived(fcnt)
158                    };
159                }
160            }
161        }
162        Response::NoUpdate
163    }
164
165    pub(crate) fn rx2_complete(&mut self) -> Response {
166        // Until we handle NbTrans, there is no case where we should not increment FCntUp.
167        if self.fcnt_up == 0xFFFF_FFFF {
168            // if the FCnt is used up, the session has expired
169            return Response::SessionExpired;
170        } else {
171            self.fcnt_up += 1;
172        }
173        if self.confirmed {
174            Response::NoAck
175        } else {
176            Response::RxComplete
177        }
178    }
179
180    pub(crate) fn prepare_buffer<C: CryptoFactory + Default, const N: usize>(
181        &mut self,
182        data: &SendData,
183        tx_buffer: &mut RadioBuffer<N>,
184    ) -> FcntUp {
185        tx_buffer.clear();
186        let fcnt = self.fcnt_up;
187        let mut phy: DataPayloadCreator<GenericArray<u8, U256>, C> = DataPayloadCreator::default();
188
189        let mut fctrl = FCtrl(0x0, true);
190        if self.uplink.confirms_downlink() {
191            fctrl.set_ack();
192            self.uplink.clear_downlink_confirmation();
193        }
194
195        self.confirmed = data.confirmed;
196
197        phy.set_confirmed(data.confirmed)
198            .set_fctrl(&fctrl)
199            .set_f_port(data.fport)
200            .set_dev_addr(self.devaddr)
201            .set_fcnt(fcnt);
202
203        let mut cmds = Vec::new();
204        self.uplink.get_cmds(&mut cmds);
205        let mut dyn_cmds: Vec<&dyn SerializableMacCommand, 8> = Vec::new();
206
207        for cmd in &cmds {
208            if let Err(_e) = dyn_cmds.push(cmd) {
209                panic!("dyn_cmds too small compared to cmds")
210            }
211        }
212
213        match phy.build(data.data, dyn_cmds.as_slice(), &self.newskey, &self.appskey) {
214            Ok(packet) => {
215                tx_buffer.clear();
216                tx_buffer.extend_from_slice(packet).unwrap();
217            }
218            Err(e) => panic!("Error assembling packet! {:?} ", e),
219        }
220        fcnt
221    }
222}