lrwn/
phy_payload.rs

1#[cfg(feature = "crypto")]
2use aes::{
3    cipher::{generic_array::GenericArray, BlockDecrypt, BlockEncrypt},
4    Aes128, Block,
5};
6use anyhow::Result;
7#[cfg(feature = "crypto")]
8use cmac::{Cmac, Mac};
9#[cfg(feature = "serde")]
10use serde::Serialize;
11
12use super::maccommand::{MACCommand, MACCommandSet};
13use super::mhdr::{FType, MHDR};
14use super::payload::{FRMPayload, MACPayload, Payload};
15#[cfg(feature = "crypto")]
16use super::{
17    aes128::AES128Key,
18    devaddr::DevAddr,
19    eui64::EUI64,
20    payload::{JoinAcceptPayload, JoinType},
21};
22use crate::relay::{ForwardDownlinkReq, ForwardUplinkReq};
23use crate::LA_FPORT_RELAY;
24
25#[derive(PartialEq, Eq, Clone, Copy, Debug)]
26#[cfg_attr(feature = "serde", derive(Serialize))]
27pub enum MACVersion {
28    LoRaWAN1_0,
29    LoRaWAN1_1,
30}
31
32/// PhyPayload represents the LoRaWAN PHY payload.
33///
34/// Join-request example:
35/// ```rust
36/// use std::str::FromStr;
37/// use lrwn::*;
38///
39/// let app_key = AES128Key::from_str("0102030405060708090a0b0c0d0e0f10").unwrap();
40///
41/// let mut phy = PhyPayload {
42///     mhdr: MHDR {
43///         f_type: FType::JoinRequest,
44///         major: Major::LoRaWANR1,
45///     },
46///     payload: Payload::JoinRequest(JoinRequestPayload {
47///         join_eui: EUI64::from_str("0101010101010101").unwrap(),
48///         dev_eui: EUI64::from_str("0202020202020202").unwrap(),
49///         dev_nonce: 771,
50///     }),
51///     mic: None,
52/// };
53///
54/// phy.set_join_request_mic(&app_key).unwrap();
55/// assert_eq!([0x9, 0xb9, 0x7b, 0x32], phy.mic.unwrap());
56///
57/// let bytes = phy.to_vec().unwrap();
58/// assert_eq!(vec![0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x09, 0xb9, 0x7b, 0x32], bytes);
59///
60/// let phy_decoded = PhyPayload::from_slice(&bytes).unwrap();
61/// assert_eq!(phy, phy_decoded);
62/// assert_eq!(true, phy_decoded.validate_join_request_mic(&app_key).unwrap());
63/// ```
64///
65/// LoRaWAN 1.0.x Join-accept example:
66/// ```rust
67/// use std::str::FromStr;
68/// use lrwn::*;
69///
70/// let app_key = AES128Key::from_str("0102030405060708090a0b0c0d0e0f10").unwrap();
71/// let join_eui = EUI64::from_str("0807060504030201").unwrap();
72/// let dev_nonce = 258;
73///
74/// let mut phy = PhyPayload {
75///     mhdr: MHDR {
76///         f_type: FType::JoinAccept,
77///         major: Major::LoRaWANR1,
78///     },
79///     payload: Payload::JoinAccept(JoinAcceptPayload {
80///         join_nonce: 65793,
81///         home_netid: NetID::from_str("020202").unwrap(),
82///         devaddr: DevAddr::from_str("01020304").unwrap(),
83///         dl_settings: DLSettings {
84///             opt_neg: false,
85///             rx2_dr: 0,
86///             rx1_dr_offset: 0,
87///         },
88///         cflist: None,
89///         rx_delay: 0,
90///     }),
91///     mic: None,
92/// };
93///
94/// phy.set_join_accept_mic(JoinType::Join, &join_eui, dev_nonce, &app_key).unwrap();
95/// assert_eq!([0x34, 0x49, 0xf2, 0x12], phy.mic.unwrap());
96///
97/// phy.encrypt_join_accept_payload(&app_key).unwrap();
98///
99/// let bytes = phy.to_vec().unwrap();
100/// assert_eq!(vec![0x20, 0x23, 0xcf, 0x33, 0x54, 0x89, 0xaa, 0xe3, 0x18, 0x3c, 0x0b, 0xe0, 0xba, 0xa8, 0xde, 0xe5, 0xf3], bytes);
101///
102/// let mut phy_decoded = PhyPayload::from_slice(&bytes).unwrap();
103/// phy_decoded.decrypt_join_accept_payload(&app_key).unwrap();
104/// assert_eq!(true, phy_decoded.validate_join_accept_mic(JoinType::Join, &join_eui, dev_nonce, &app_key).unwrap());
105///
106/// assert_eq!(PhyPayload {
107///     mhdr: MHDR {
108///         f_type: FType::JoinAccept,
109///         major: Major::LoRaWANR1,
110///     },
111///     payload: Payload::JoinAccept(JoinAcceptPayload {
112///         join_nonce: 65793,
113///         home_netid: NetID::from_str("020202").unwrap(),
114///         devaddr: DevAddr::from_str("01020304").unwrap(),
115///         dl_settings: DLSettings {
116///             opt_neg: false,
117///             rx2_dr: 0,
118///             rx1_dr_offset: 0,
119///         },
120///         cflist: None,
121///         rx_delay: 0,
122///     }),
123///     mic: Some([0x34, 0x49, 0xf2, 0x12]),
124/// }, phy_decoded);
125/// ```
126///
127/// LoRaWAN 1.1.x Join-accept example:
128/// ```rust
129/// use std::str::FromStr;
130/// use lrwn::*;
131///
132/// let app_key = AES128Key::from_str("0102030405060708090a0b0c0d0e0f10").unwrap();
133/// let join_eui = EUI64::from_str("0807060504030201").unwrap();
134/// let dev_nonce = 258;
135///
136/// let mut phy = PhyPayload {
137///     mhdr: MHDR {
138///         f_type: FType::JoinAccept,
139///         major: Major::LoRaWANR1,
140///     },
141///     payload: Payload::JoinAccept(JoinAcceptPayload {
142///         join_nonce: 65793,
143///         home_netid: NetID::from_str("020202").unwrap(),
144///         devaddr: DevAddr::from_str("01020304").unwrap(),
145///         dl_settings: DLSettings {
146///             opt_neg: true, // Note that opt_neg is set to true!
147///             rx2_dr: 0,
148///             rx1_dr_offset: 0,
149///         },
150///         cflist: None,
151///         rx_delay: 0,
152///     }),
153///     mic: None,
154/// };
155///
156/// phy.set_join_accept_mic(JoinType::Join, &join_eui, dev_nonce, &app_key).unwrap();
157/// assert_eq!([0x93, 0xff, 0x9a, 0x3a], phy.mic.unwrap());
158///
159/// phy.encrypt_join_accept_payload(&app_key).unwrap();
160///
161/// let bytes = phy.to_vec().unwrap();
162/// assert_eq!(vec![0x20, 0x7a, 0xbe, 0xea, 0x06, 0xb0, 0x29, 0x20, 0xf1, 0x1c, 0x02, 0xd0, 0x34, 0x8f, 0xcf, 0x18, 0x15], bytes);
163///
164/// let mut phy_decoded = PhyPayload::from_slice(&bytes).unwrap();
165/// phy_decoded.decrypt_join_accept_payload(&app_key).unwrap();
166/// assert_eq!(true, phy_decoded.validate_join_accept_mic(JoinType::Join, &join_eui, dev_nonce, &app_key).unwrap());
167///
168/// assert_eq!(PhyPayload {
169///     mhdr: MHDR {
170///         f_type: FType::JoinAccept,
171///         major: Major::LoRaWANR1,
172///     },
173///     payload: Payload::JoinAccept(JoinAcceptPayload {
174///         join_nonce: 65793,
175///         home_netid: NetID::from_str("020202").unwrap(),
176///         devaddr: DevAddr::from_str("01020304").unwrap(),
177///         dl_settings: DLSettings {
178///             opt_neg: true,
179///             rx2_dr: 0,
180///             rx1_dr_offset: 0,
181///         },
182///         cflist: None,
183///         rx_delay: 0,
184///     }),
185///     mic: Some([0x93, 0xff, 0x9a, 0x3a]),
186/// }, phy_decoded);
187/// ```
188///
189/// LoRaWAN 1.0.x confirmed uplink example:
190/// ```rust
191/// use std::str::FromStr;
192/// use lrwn::*;
193///
194/// let nwk_s_key = AES128Key::from_str("0102030405060708090a0b0c0d0e0f10").unwrap();
195/// let app_s_key = AES128Key::from_str("100f0e0d0c0b0a090807060504030201").unwrap();
196///
197/// let mut phy = PhyPayload {
198///     mhdr: MHDR {
199///         f_type: FType::ConfirmedDataUp,
200///         major: Major::LoRaWANR1,
201///     },
202///     payload: Payload::MACPayload(MACPayload{
203///         fhdr: FHDR{
204///             devaddr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
205///             f_ctrl: FCtrl::default(),
206///             f_cnt: 0,
207///             f_opts: MACCommandSet::new(vec![
208///                 MACCommand::DevStatusAns(DevStatusAnsPayload{
209///                     battery: 115,
210///                     margin: 7,
211///                 }),
212///             ]),
213///         },
214///         f_port: Some(10),
215///         frm_payload: Some(FRMPayload::Raw(vec![0x01, 0x02, 0x03, 0x04])),
216///     }),
217///     mic: None,
218/// };
219///
220/// phy.encrypt_frm_payload(&app_s_key).unwrap();
221/// phy.set_uplink_data_mic(MACVersion::LoRaWAN1_0, 0, 0, 0, &nwk_s_key, &nwk_s_key);
222///
223/// let bytes = phy.to_vec().unwrap();
224/// assert_eq!(vec![0x80, 0x04, 0x03, 0x02, 0x01, 0x03, 0x00, 0x00, 0x06, 0x73, 0x07, 0x0a, 0xe2, 0x64, 0xd4, 0xf7, 0xe1, 0x17, 0xd2, 0xc0], bytes);
225///
226/// let mut phy_decoded = PhyPayload::from_slice(&bytes).unwrap();
227/// assert_eq!(true, phy_decoded.validate_uplink_data_mic(MACVersion::LoRaWAN1_0, 0, 0, 0, &nwk_s_key, &nwk_s_key).unwrap());
228///
229/// phy_decoded.decrypt_frm_payload(&app_s_key).unwrap();
230///
231/// if let Payload::MACPayload(pl) = &phy_decoded.payload {
232///     if let FRMPayload::Raw(b) = &pl.frm_payload.as_ref().unwrap() {
233///         assert_eq!(&vec![0x01, 0x02, 0x03, 0x04], b);
234///     } else {
235///         panic!("No FrmPayload!");
236///     }
237/// } else {
238///     panic!("No MacPayload!");
239/// }
240/// ```
241///
242/// LoRaWAN 1.1.x downlink with encrypted f_opts example:
243/// ```rust
244/// use std::str::FromStr;
245/// use lrwn::*;
246///
247/// let s_nwk_s_int_key = AES128Key::from_str("01010101010101010101010101010100").unwrap();
248/// let nwk_s_enc_key = AES128Key::from_str("01010101010101010101010101010200").unwrap();
249/// let app_s_key = AES128Key::from_str("100f0e0d0c0b0a090807060504030201").unwrap();
250///
251/// let mut phy = PhyPayload {
252///     mhdr: MHDR{
253///         f_type: FType::UnconfirmedDataDown,
254///         major: Major::LoRaWANR1,
255///     },
256///     payload: Payload::MACPayload(MACPayload{
257///         fhdr: FHDR{
258///             devaddr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
259///             f_ctrl: FCtrl::default(),
260///             f_cnt: 0,
261///             f_opts: MACCommandSet::new(vec![
262///                 MACCommand::LinkCheckAns(LinkCheckAnsPayload{
263///                     margin: 7,
264///                     gw_cnt: 1,
265///                 }),
266///             ]),
267///         },
268///         f_port: Some(1),
269///         frm_payload: Some(FRMPayload::Raw(vec![0x01, 0x02, 0x03, 0x04])),
270///     }),
271///     mic: None,
272/// };
273///
274/// phy.encrypt_f_opts(&nwk_s_enc_key).unwrap();
275/// phy.encrypt_frm_payload(&app_s_key).unwrap();
276/// phy.set_downlink_data_mic(MACVersion::LoRaWAN1_1, 0, &s_nwk_s_int_key).unwrap();
277///
278/// let bytes = phy.to_vec().unwrap();
279/// assert_eq!(vec![0x60, 0x04, 0x03, 0x02, 0x01, 0x03, 0x00, 0x00, 0x22, 0xac, 0x0a, 0x01, 0xf0, 0xb4, 0x68, 0xdd, 0xaa, 0x5e, 0xd1, 0x3a], bytes);
280///
281/// let mut phy_decoded = PhyPayload::from_slice(&bytes).unwrap();
282/// assert_eq!(true, phy_decoded.validate_downlink_data_mic(MACVersion::LoRaWAN1_1, 0, &s_nwk_s_int_key).unwrap());
283///
284/// phy_decoded.decrypt_f_opts(&nwk_s_enc_key).unwrap();
285/// phy_decoded.decrypt_frm_payload(&app_s_key).unwrap();
286///
287/// if let Payload::MACPayload(pl) = &phy_decoded.payload {
288///     assert_eq!(MACCommandSet::new(vec![
289///         MACCommand::LinkCheckAns(LinkCheckAnsPayload{
290///             margin: 7,
291///             gw_cnt: 1,
292///         }),
293///     ]), pl.fhdr.f_opts);
294///
295///     if let FRMPayload::Raw(b) = &pl.frm_payload.as_ref().unwrap() {
296///         assert_eq!(&vec![0x01, 0x02, 0x03, 0x04], b);
297///     } else {
298///         panic!("No FrmPayload!");
299///     }
300/// } else {
301///     panic!("No MacPayload");
302/// }
303/// ```
304///
305/// Proprietary example:
306/// ```rust
307/// use std::str::FromStr;
308/// use lrwn::*;
309///
310/// let phy = PhyPayload {
311///     mhdr: MHDR {
312///         f_type: FType::Proprietary,
313///         major: Major::LoRaWANR1,
314///     },
315///     payload: Payload::Raw(vec![0x01, 0x02, 0x03]),
316///     mic: None,
317/// };
318///
319/// let bytes = phy.to_vec().unwrap();
320/// assert_eq!(vec![0xe0, 0x01, 0x02, 0x03], bytes);
321///
322/// let phy_decoded = PhyPayload::from_slice(&bytes).unwrap();
323/// assert_eq!(phy, phy_decoded);
324/// ```
325///
326/// LoRaWAN 1.0.x Relay ForwardUplinkReq example:
327/// ```rust
328/// use std::str::FromStr;
329/// use lrwn::*;
330///
331/// // Payload from the end-device.
332/// let ed_app_key = AES128Key::from_str("01020304050607080102030405060708").unwrap();
333/// let mut ed_phy = PhyPayload {
334///     mhdr: MHDR {
335///         f_type: FType::JoinRequest,
336///         major: Major::LoRaWANR1,
337///     },
338///     payload: Payload::JoinRequest(JoinRequestPayload {
339///         join_eui: EUI64::from_str("0101010101010101").unwrap(),
340///         dev_eui: EUI64::from_str("0202020202020202").unwrap(),
341///         dev_nonce: 771,
342///     }),
343///     mic: None,
344/// };
345///
346/// ed_phy.set_join_request_mic(&ed_app_key).unwrap();
347///
348/// // Relay ForwardUplinkReq (which will forward the end-device payload).
349/// let relay_nwk_s_key = AES128Key::from_str("08070605040302010807060504030201").unwrap();
350/// let mut relay_phy = PhyPayload {
351///     mhdr: MHDR {
352///         f_type: FType::UnconfirmedDataUp,
353///         major: Major::LoRaWANR1,
354///     },
355///     payload: Payload::MACPayload(MACPayload {
356///         fhdr: FHDR {
357///             devaddr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
358///             f_cnt: 10,
359///             ..Default::default()
360///         },
361///         f_port: Some(226),
362///         frm_payload: Some(FRMPayload::ForwardUplinkReq(ForwardUplinkReq {
363///             metadata: UplinkMetadata {
364///                 dr: 5,
365///                 snr: 7,
366///                 rssi: -80,
367///                 wor_channel: 1,
368///             },
369///             frequency: 868100000,
370///             payload: Box::new(ed_phy.clone()),
371///         })),
372///     }),
373///     mic: None,
374/// };
375/// relay_phy.encrypt_frm_payload(&relay_nwk_s_key).unwrap();
376/// relay_phy.set_uplink_data_mic(MACVersion::LoRaWAN1_0, 0, 0, 0, &relay_nwk_s_key, &relay_nwk_s_key);
377///
378/// let bytes = relay_phy.to_vec().unwrap();
379/// assert_eq!(vec![0x40, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x00, 0xe2, 0x2f, 0x68, 0xf4, 0xa5, 0x0a, 0xdf, 0xfb, 0x64, 0xef, 0x37, 0x91, 0x0f, 0x14, 0x6a, 0x6c, 0x2b, 0xda, 0x4f, 0x7e, 0x2d, 0xb9, 0x6a, 0xc8, 0x99, 0xa8, 0xa4, 0x72, 0x7d, 0x0a, 0xbd, 0xc9, 0xae, 0x51], bytes);
380///
381/// let mut relay_phy_decoded = PhyPayload::from_slice(&bytes).unwrap();
382/// assert_eq!(relay_phy, relay_phy_decoded);
383///
384/// relay_phy_decoded.decrypt_frm_payload(&relay_nwk_s_key).unwrap();
385/// assert_eq!(PhyPayload{
386///     mhdr: MHDR {
387///         f_type: FType::UnconfirmedDataUp,
388///         major: Major::LoRaWANR1,
389///     },
390///     payload: Payload::MACPayload(MACPayload {
391///         fhdr: FHDR {
392///             devaddr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
393///             f_cnt: 10,
394///             ..Default::default()
395///         },
396///         f_port: Some(226),
397///         frm_payload: Some(FRMPayload::ForwardUplinkReq(ForwardUplinkReq {
398///             metadata: UplinkMetadata {
399///                 dr: 5,
400///                 snr: 7,
401///                 rssi: -80,
402///                 wor_channel: 1,
403///             },
404///             frequency: 868100000,
405///             payload: Box::new(ed_phy),
406///         })),
407///     }),
408///     mic: Some([0xbd, 0xc9, 0xae, 0x51]),
409/// }, relay_phy_decoded);
410/// ```
411///
412/// LoRaWAN 1.0.x Relay ForwardDownlinkReq example:
413/// ```rust
414/// use std::str::FromStr;
415/// use lrwn::*;
416///
417/// // Payload for the end-device.
418/// let ed_app_key = AES128Key::from_str("0102030405060708090a0b0c0d0e0f10").unwrap();
419/// let ed_join_eui = EUI64::from_str("0807060504030201").unwrap();
420/// let ed_dev_nonce = 258;
421/// let mut ed_phy = PhyPayload {
422///     mhdr: MHDR {
423///         f_type: FType::JoinAccept,
424///         major: Major::LoRaWANR1,
425///     },
426///     payload: Payload::JoinAccept(JoinAcceptPayload {
427///         join_nonce: 65793,
428///         home_netid: NetID::from_str("020202").unwrap(),
429///         devaddr: DevAddr::from_str("01020304").unwrap(),
430///         dl_settings: DLSettings {
431///             opt_neg: false,
432///             rx2_dr: 0,
433///             rx1_dr_offset: 0,
434///         },
435///         cflist: None,
436///         rx_delay: 0,
437///     }),
438///     mic: None,
439/// };
440///
441/// ed_phy.set_join_accept_mic(JoinType::Join, &ed_join_eui, ed_dev_nonce, &ed_app_key).unwrap();
442/// ed_phy.encrypt_join_accept_payload(&ed_app_key).unwrap();
443///
444/// // Payload for the Relay containing the ForwardDownlinkReq.
445/// let relay_nwk_s_key = AES128Key::from_str("08070605040302010807060504030201").unwrap();
446/// let mut relay_phy = PhyPayload {
447///     mhdr: MHDR {
448///         f_type: FType::UnconfirmedDataDown,
449///         major: Major::LoRaWANR1,
450///     },
451///     payload: Payload::MACPayload(MACPayload {
452///         fhdr: FHDR {
453///             devaddr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
454///             f_cnt: 10,
455///             ..Default::default()
456///         },
457///         f_port: Some(226),
458///         frm_payload: Some(FRMPayload::ForwardDownlinkReq(ForwardDownlinkReq {
459///             payload: Box::new(ed_phy.clone()),
460///         })),
461///     }),
462///     mic: None,
463/// };
464/// relay_phy.encrypt_frm_payload(&relay_nwk_s_key).unwrap();
465/// relay_phy.set_downlink_data_mic(MACVersion::LoRaWAN1_0, 0, &relay_nwk_s_key).unwrap();
466///
467/// let bytes = relay_phy.to_vec().unwrap();
468/// assert_eq!(vec![0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x00, 0xe2, 0xc9, 0x60, 0x41, 0x64, 0xc9, 0x7d, 0x76, 0xf9, 0xea, 0x8e, 0x1a, 0x79, 0x2b, 0xa0, 0x87, 0x9b, 0x85, 0x24, 0x3e, 0x5a, 0xf5], bytes);
469///
470/// let mut relay_phy_decoded = PhyPayload::from_slice(&bytes).unwrap();
471/// assert_eq!(relay_phy, relay_phy_decoded);
472///
473/// relay_phy_decoded.decrypt_frm_payload(&relay_nwk_s_key).unwrap();
474/// assert_eq!(PhyPayload {
475///     mhdr: MHDR {
476///         f_type: FType::UnconfirmedDataDown,
477///         major: Major::LoRaWANR1,
478///     },
479///     payload: Payload::MACPayload(MACPayload {
480///         fhdr: FHDR {
481///             devaddr: DevAddr::from_be_bytes([0x01, 0x02, 0x03, 0x04]),
482///             f_cnt: 10,
483///             ..Default::default()
484///         },
485///         f_port: Some(226),
486///         frm_payload: Some(FRMPayload::ForwardDownlinkReq(ForwardDownlinkReq {
487///             payload: Box::new(ed_phy),
488///         })),
489///     }),
490///     mic: Some([0x24, 0x3e, 0x5a, 0xf5]),
491/// }, relay_phy_decoded);
492/// ```
493#[derive(Debug, PartialEq, Eq, Clone)]
494#[cfg_attr(feature = "serde", derive(Serialize))]
495pub struct PhyPayload {
496    pub mhdr: MHDR,
497    pub payload: Payload,
498    /// This field is not used in case of the proprietary message-type.
499    pub mic: Option<[u8; 4]>,
500}
501
502impl PhyPayload {
503    pub fn to_vec(&self) -> Result<Vec<u8>> {
504        let mut b = Vec::new();
505
506        b.extend_from_slice(&self.mhdr.to_le_bytes());
507        b.append(&mut self.payload.to_vec()?);
508
509        if let Some(v) = &self.mic {
510            b.extend_from_slice(&v.clone());
511        }
512
513        Ok(b)
514    }
515
516    pub fn from_slice(b: &[u8]) -> Result<Self> {
517        let b_len = b.len();
518
519        // We need 1 byte to decode the mhdr.
520        if b_len == 0 {
521            return Err(anyhow!("at least 1 byte required to decode PhyPayload"));
522        }
523
524        let mhdr = {
525            let mhdr: [u8; 1] = [b[0]];
526            MHDR::from_le_bytes(mhdr)?
527        };
528
529        if mhdr.f_type == FType::Proprietary {
530            return Ok(PhyPayload {
531                mhdr,
532                payload: Payload::from_slice(FType::Proprietary, &b[1..])?,
533                mic: None,
534            });
535        }
536
537        // Validate the minimum required bytes for not running into slicing errors.
538        if b_len < 5 {
539            return Err(anyhow!(
540                "at least 5 bytes are required to decode PhyPayload"
541            ));
542        }
543
544        let f_type = mhdr.f_type;
545        let mut mic: [u8; 4] = [0; 4];
546        mic.clone_from_slice(&b[b_len - 4..]);
547
548        Ok(PhyPayload {
549            mhdr,
550            payload: Payload::from_slice(f_type, &b[1..b_len - 4])?,
551            mic: Some(mic),
552        })
553    }
554
555    /// Calculate and set the MIC field for uplink data frames.
556    /// The conf_f_cnt, tx_dr, tx_ch and s_nwk_s_int_key are only required for LoRaWAN 1.1 and can
557    /// be left blank for LoRaWAN 1.0.
558    #[cfg(feature = "crypto")]
559    pub fn set_uplink_data_mic(
560        &mut self,
561        mac_version: MACVersion,
562        conf_f_cnt: u32,
563        tx_dr: u8,
564        tx_ch: u8,
565        f_nwk_s_int_key: &AES128Key,
566        s_nwk_s_int_key: &AES128Key,
567    ) -> Result<()> {
568        self.mic = Some(self.calculate_uplink_data_mic(
569            mac_version,
570            conf_f_cnt,
571            tx_dr,
572            tx_ch,
573            f_nwk_s_int_key,
574            s_nwk_s_int_key,
575        )?);
576        Ok(())
577    }
578
579    /// Validate the MIC of an uplink data frame.
580    /// In order to validate the MIC, the f_cnt value must be first set to the full 32 bit
581    /// frame-counter value, as only the 16 lsb are transmitted over the air.
582    /// The conf_f_cnt, tx_dr, tx_ch and s_nwk_s_int_key are only required for LoRaWAN 1.1 and can
583    /// be left blank for LoRaWAN 1.0.
584    #[cfg(feature = "crypto")]
585    pub fn validate_uplink_data_mic(
586        &self,
587        mac_version: MACVersion,
588        conf_f_cnt: u32,
589        tx_dr: u8,
590        tx_ch: u8,
591        f_nwk_s_int_key: &AES128Key,
592        s_nwk_s_int_key: &AES128Key,
593    ) -> Result<bool> {
594        if let Some(mic) = self.mic {
595            return Ok(mic
596                == self.calculate_uplink_data_mic(
597                    mac_version,
598                    conf_f_cnt,
599                    tx_dr,
600                    tx_ch,
601                    f_nwk_s_int_key,
602                    s_nwk_s_int_key,
603                )?);
604        }
605
606        Ok(false)
607    }
608
609    /// Set the MIC for downlink data frames.
610    /// The conf_f_cnt is only required for LoRaWAN 1.1 and can be left blank for LoRaWAN 1.0.
611    #[cfg(feature = "crypto")]
612    pub fn set_downlink_data_mic(
613        &mut self,
614        mac_version: MACVersion,
615        conf_f_cnt: u32,
616        s_nwk_s_int_key: &AES128Key,
617    ) -> Result<()> {
618        self.mic =
619            Some(self.calculate_downlink_data_mic(mac_version, conf_f_cnt, s_nwk_s_int_key)?);
620        Ok(())
621    }
622
623    #[cfg(feature = "crypto")]
624    pub fn validate_downlink_data_mic(
625        &mut self,
626        mac_version: MACVersion,
627        conf_f_cnt: u32,
628        s_nwk_s_int_key: &AES128Key,
629    ) -> Result<bool> {
630        if let Some(mic) = self.mic {
631            return Ok(mic
632                == self.calculate_downlink_data_mic(mac_version, conf_f_cnt, s_nwk_s_int_key)?);
633        }
634
635        Ok(false)
636    }
637
638    /// Validate the cmacF part of the uplink data MIC (LoRaWAN 1.1 only).
639    /// In order to validate the MIC, the f_cnt value must be first set to the full 32 bit
640    /// frame-counter value, as only the 16 lsb are transmitted over the air.
641    #[cfg(feature = "crypto")]
642    pub fn validate_uplink_data_micf(&self, f_nwk_s_int_key: &AES128Key) -> Result<bool> {
643        // We are only interested in mic[2:] (cmacF bytes), therefore there is no
644        // need to pass the correct confFCnt, txDR, txCh and sNwkSIntKey parameters.
645        if let Some(v) = self.mic {
646            let mic = self.calculate_uplink_data_mic(
647                MACVersion::LoRaWAN1_1,
648                0,
649                0,
650                0,
651                f_nwk_s_int_key,
652                f_nwk_s_int_key,
653            )?;
654
655            return Ok(v[2..] == mic[2..]);
656        }
657
658        Ok(false)
659    }
660
661    /// Set the join-request MIC.
662    #[cfg(feature = "crypto")]
663    pub fn set_join_request_mic(&mut self, key: &AES128Key) -> Result<()> {
664        self.mic = Some(self.calculate_upink_join_mic(key)?);
665        Ok(())
666    }
667
668    /// Validate the join-request MIC.
669    #[cfg(feature = "crypto")]
670    pub fn validate_join_request_mic(&self, key: &AES128Key) -> Result<bool> {
671        if let Some(v) = self.mic {
672            let mic = self.calculate_upink_join_mic(key)?;
673            return Ok(v == mic);
674        }
675
676        Ok(false)
677    }
678
679    /// Set the the downlink join-accept MIC.
680    #[cfg(feature = "crypto")]
681    pub fn set_join_accept_mic(
682        &mut self,
683        join_req_type: JoinType,
684        join_eui: &EUI64,
685        dev_nonce: u16,
686        key: &AES128Key,
687    ) -> Result<()> {
688        self.mic =
689            Some(self.calculate_downlink_join_mic(join_req_type, join_eui, dev_nonce, key)?);
690        Ok(())
691    }
692
693    /// Validate the downlink join-accept MIC.
694    #[cfg(feature = "crypto")]
695    pub fn validate_join_accept_mic(
696        &self,
697        join_req_type: JoinType,
698        join_eui: &EUI64,
699        dev_nonce: u16,
700        key: &AES128Key,
701    ) -> Result<bool> {
702        if let Some(v) = self.mic {
703            let mic = self.calculate_downlink_join_mic(join_req_type, join_eui, dev_nonce, key)?;
704            return Ok(v == mic);
705        }
706
707        Ok(false)
708    }
709
710    /// Encrypt the join-accept payload with the given key.
711    /// Note that the encryption must be performed after setting the MIC, since the MIC is part of
712    /// the encrypted payload.
713    /// For encrypting a join-request response, use the nwk_key, for rejoin-request 0, 1 and 3
714    /// response, use the js_enc_key.
715    #[cfg(feature = "crypto")]
716    pub fn encrypt_join_accept_payload(&mut self, key: &AES128Key) -> Result<()> {
717        use aes::cipher::KeyInit;
718
719        if self.mic.is_none() {
720            return Err(anyhow!("mic must be set first"));
721        }
722
723        if let Payload::JoinAccept(pl) = &self.payload {
724            let mut pt = pl.to_vec()?;
725            pt.extend_from_slice(&self.mic.unwrap());
726
727            if pt.len() % 16 != 0 {
728                return Err(anyhow!("plaintext must be a multiple of 16 bytes"));
729            }
730
731            let key_bytes = key.to_bytes();
732            let key = GenericArray::from_slice(&key_bytes);
733            let cipher = Aes128::new(key);
734
735            let mut ct = Vec::new();
736
737            for i in 0..(pt.len() / 16) {
738                let index = i * 16;
739
740                let mut block = Block::clone_from_slice(&pt[index..index + 16]);
741                cipher.decrypt_block(&mut block);
742                ct.extend_from_slice(block.as_slice());
743            }
744
745            self.payload = Payload::Raw(ct[0..ct.len() - 4].to_vec());
746            let mut mic: [u8; 4] = [0; 4];
747            mic.clone_from_slice(&ct[ct.len() - 4..]);
748            self.mic = Some(mic);
749            return Ok(());
750        }
751
752        Err(anyhow!("payload must be of type JoinAcceptPayload"))
753    }
754
755    /// Decrypt the join-accept payload with the given key.
756    /// Note that the decryption must be performed before validating the MIC, since the MIC is part
757    /// of the encrypted payload.
758    /// For decrypting a join-request response, use the nwk_key, for rejoin-request 0, 1 and 3
759    /// response, use the js_enc_key.
760    #[cfg(feature = "crypto")]
761    pub fn decrypt_join_accept_payload(&mut self, key: &AES128Key) -> Result<()> {
762        use aes::cipher::KeyInit;
763
764        if self.mic.is_none() {
765            return Err(anyhow!("mic must be set first"));
766        }
767
768        if let Payload::Raw(pl) = &self.payload {
769            // append MIC since it is encrypted too
770            let mut ct = pl.clone();
771            ct.extend_from_slice(&self.mic.unwrap());
772
773            if ct.len() % 16 != 0 {
774                return Err(anyhow!("ciphertext must be a multiple of 16 bytes"));
775            }
776
777            let key_bytes = key.to_bytes();
778            let key = GenericArray::from_slice(&key_bytes);
779            let cipher = Aes128::new(key);
780
781            let mut pt = Vec::new();
782
783            for i in 0..(ct.len() / 16) {
784                let index = i * 16;
785
786                let mut block = Block::clone_from_slice(&ct[index..index + 16]);
787                cipher.encrypt_block(&mut block);
788                pt.extend_from_slice(block.as_slice());
789            }
790
791            let mut mic: [u8; 4] = [0; 4];
792            mic.clone_from_slice(&pt[pt.len() - 4..]);
793            self.mic = Some(mic);
794            self.payload = Payload::JoinAccept(JoinAcceptPayload::from_slice(&pt[..pt.len() - 4])?);
795
796            return Ok(());
797        }
798
799        Err(anyhow!("payload must be of type Raw"))
800    }
801
802    /// Encrypt the f_opts with the given key.
803    #[cfg(feature = "crypto")]
804    pub fn encrypt_f_opts(&mut self, nwk_s_enc_key: &AES128Key) -> Result<()> {
805        if let Payload::MACPayload(pl) = &mut self.payload {
806            let f_opts_bytes = pl.fhdr.f_opts.to_vec()?;
807            if f_opts_bytes.is_empty() {
808                return Ok(());
809            }
810
811            let uplink = is_uplink(self.mhdr.f_type);
812
813            // a_fcnt_down is used on downlink when f_port > 0
814            let a_fcnt_down = !uplink && pl.f_port.is_some() && pl.f_port.unwrap() > 0;
815
816            let f_opts_enc = encrypt_f_opts(
817                nwk_s_enc_key,
818                a_fcnt_down,
819                uplink,
820                &pl.fhdr.devaddr,
821                pl.fhdr.f_cnt,
822                &f_opts_bytes,
823            )?;
824
825            pl.fhdr.f_opts = MACCommandSet::new(vec![MACCommand::Raw(f_opts_enc)]);
826            return Ok(());
827        }
828
829        Err(anyhow!("payload must be of type MACPayload"))
830    }
831
832    /// Decrypt the f_opts with the given key.
833    /// This automatically calls decode_f_opts_to_mac_commands.
834    #[cfg(feature = "crypto")]
835    pub fn decrypt_f_opts(&mut self, nwk_s_enc_key: &AES128Key) -> Result<()> {
836        self.encrypt_f_opts(nwk_s_enc_key)?;
837        self.decode_f_opts_to_mac_commands()?;
838
839        Ok(())
840    }
841
842    /// Decode f_opts to mac-commands.
843    pub fn decode_f_opts_to_mac_commands(&mut self) -> Result<()> {
844        if let Payload::MACPayload(pl) = &mut self.payload {
845            let uplink = is_uplink(self.mhdr.f_type);
846            pl.fhdr.f_opts.decode_from_raw(uplink)?;
847        }
848        Ok(())
849    }
850
851    /// Decode frm_payload payload.
852    ///
853    /// This will decode as follow based on f_port:
854    /// 0:   MACCommandSet
855    /// 226: ForwardDownlinkReq / ForwardDownlinkReq
856    ///
857    /// For other f_port values, it will not try to decode the payload.
858    /// Note that this requires a decrypted frm_payload.
859    pub fn decode_frm_payload(&mut self) -> Result<()> {
860        if let Payload::MACPayload(pl) = &mut self.payload {
861            let uplink = is_uplink(self.mhdr.f_type);
862            let f_port = pl.f_port.unwrap_or(0);
863            let b = match &pl.frm_payload {
864                Some(FRMPayload::Raw(v)) => v.clone(),
865                _ => {
866                    // Nothing to do.
867                    return Ok(());
868                }
869            };
870
871            return decode_frm_payload(pl, uplink, f_port, b);
872        }
873
874        Ok(())
875    }
876
877    /// Encrypt the frm_payload with the given key.
878    #[cfg(feature = "crypto")]
879    pub fn encrypt_frm_payload(&mut self, key: &AES128Key) -> Result<()> {
880        if let Payload::MACPayload(pl) = &mut self.payload {
881            // nothing to do
882            if pl.frm_payload.is_none() {
883                return Ok(());
884            }
885
886            let uplink = is_uplink(self.mhdr.f_type);
887            let data = pl.frm_payload.as_ref().unwrap().to_vec()?;
888            let data = encrypt_frm_payload(key, uplink, &pl.fhdr.devaddr, pl.fhdr.f_cnt, &data)?;
889
890            pl.frm_payload = Some(FRMPayload::Raw(data));
891            return Ok(());
892        }
893
894        Err(anyhow!("payload must be of type MACPayload"))
895    }
896
897    /// Decrypt the frm_payload with the given key.
898    ///
899    /// This will automatically call decode_frm_payload.
900    #[cfg(feature = "crypto")]
901    pub fn decrypt_frm_payload(&mut self, key: &AES128Key) -> Result<()> {
902        if let Payload::MACPayload(pl) = &mut self.payload {
903            // nothing to do
904            if pl.frm_payload.is_none() {
905                return Ok(());
906            }
907
908            let uplink = is_uplink(self.mhdr.f_type);
909            let data = pl.frm_payload.as_ref().unwrap().to_vec()?;
910            let data = encrypt_frm_payload(key, uplink, &pl.fhdr.devaddr, pl.fhdr.f_cnt, &data)?;
911
912            return decode_frm_payload(pl, uplink, pl.f_port.unwrap_or(0), data);
913        }
914
915        Err(anyhow!("payload must be of type MACPayload"))
916    }
917
918    #[cfg(feature = "crypto")]
919    fn calculate_uplink_data_mic(
920        &self,
921        mac_version: MACVersion,
922        conf_f_cnt: u32,
923        tx_dr: u8,
924        tx_ch: u8,
925        f_nwk_s_int_key: &AES128Key,
926        s_nwk_s_int_key: &AES128Key,
927    ) -> Result<[u8; 4]> {
928        if let Payload::MACPayload(pl) = &self.payload {
929            // set to 0 if the uplink does not contain an ACK
930            let mut conf_f_cnt = conf_f_cnt;
931            if !pl.fhdr.f_ctrl.ack {
932                conf_f_cnt = 0;
933            }
934
935            // truncate to 16 lsb
936            let conf_f_cnt = (conf_f_cnt % (1 << 16)) as u16;
937
938            let mut mic_bytes = Vec::new();
939            mic_bytes.extend_from_slice(&self.mhdr.to_le_bytes());
940            mic_bytes.extend_from_slice(&self.payload.to_vec()?);
941
942            let mut b0: [u8; 16] = [0; 16];
943            let mut b1: [u8; 16] = [0; 16];
944
945            b0[0] = 0x49;
946            b1[0] = 0x49;
947
948            // devaddr
949            let devaddr_b = pl.fhdr.devaddr.to_le_bytes();
950            b0[6..10].clone_from_slice(&devaddr_b);
951            b1[6..10].clone_from_slice(&devaddr_b);
952
953            // fcntup
954            b0[10..14].clone_from_slice(&pl.fhdr.f_cnt.to_le_bytes());
955            b1[10..14].clone_from_slice(&pl.fhdr.f_cnt.to_le_bytes());
956
957            // msg len
958            b0[15] = mic_bytes.len() as u8;
959            b1[15] = mic_bytes.len() as u8;
960
961            // remaining b1 fields
962            b1[1..3].clone_from_slice(&conf_f_cnt.to_le_bytes());
963            b1[3] = tx_dr;
964            b1[4] = tx_ch;
965
966            let mut mac = Cmac::<Aes128>::new_from_slice(&s_nwk_s_int_key.to_bytes()).unwrap();
967            mac.update(&b1);
968            mac.update(&mic_bytes);
969
970            let cmac_s = mac.finalize().into_bytes();
971            if cmac_s.len() < 4 {
972                return Err(anyhow!("cmac_s is less than 4 bytes"));
973            }
974
975            let mut mac = Cmac::<Aes128>::new_from_slice(&f_nwk_s_int_key.to_bytes()).unwrap();
976            mac.update(&b0);
977            mac.update(&mic_bytes);
978
979            let cmac_f = mac.finalize().into_bytes();
980            if cmac_f.len() < 4 {
981                return Err(anyhow!("cmac_f is less than 4 bytes"));
982            }
983
984            let mut mic: [u8; 4] = [0; 4];
985            if mac_version == MACVersion::LoRaWAN1_0 {
986                mic.clone_from_slice(&cmac_f[0..4]);
987                return Ok(mic);
988            } else {
989                mic[0..2].clone_from_slice(&cmac_s[0..2]);
990                mic[2..4].clone_from_slice(&cmac_f[0..2]);
991                return Ok(mic);
992            }
993        }
994
995        Err(anyhow!("payload must be of type MACPayload"))
996    }
997
998    #[cfg(feature = "crypto")]
999    fn calculate_downlink_data_mic(
1000        &self,
1001        mac_version: MACVersion,
1002        conf_f_cnt: u32,
1003        s_nwk_s_int_key: &AES128Key,
1004    ) -> Result<[u8; 4]> {
1005        if let Payload::MACPayload(pl) = &self.payload {
1006            // set to 0 if the downlink does not contain an ack or in case of LoRaWAN 1.0
1007            let mut conf_f_cnt = conf_f_cnt;
1008            if mac_version == MACVersion::LoRaWAN1_0 || !pl.fhdr.f_ctrl.ack {
1009                conf_f_cnt = 0;
1010            }
1011
1012            // truncate to 16 lsb
1013            let conf_f_cnt = (conf_f_cnt % (1 << 16)) as u16;
1014
1015            // mic bytes
1016            let mut mic_bytes = Vec::new();
1017            mic_bytes.extend_from_slice(&self.mhdr.to_le_bytes());
1018            mic_bytes.extend_from_slice(&self.payload.to_vec()?);
1019
1020            // b0
1021            let mut b0: [u8; 16] = [0; 16];
1022            b0[0] = 0x49;
1023            b0[1..3].clone_from_slice(&conf_f_cnt.to_le_bytes());
1024            b0[5] = 0x01;
1025            b0[6..10].clone_from_slice(&pl.fhdr.devaddr.to_le_bytes());
1026            b0[10..14].clone_from_slice(&pl.fhdr.f_cnt.to_le_bytes());
1027            b0[15] = mic_bytes.len() as u8;
1028
1029            let mut mac = Cmac::<Aes128>::new_from_slice(&s_nwk_s_int_key.to_bytes()).unwrap();
1030            mac.update(&b0);
1031            mac.update(&mic_bytes);
1032
1033            let hash = mac.finalize().into_bytes();
1034            if hash.len() < 4 {
1035                return Err(anyhow!("hash is less than 4 bytes"));
1036            }
1037
1038            let mut mic: [u8; 4] = [0; 4];
1039            mic.clone_from_slice(&hash[0..4]);
1040            return Ok(mic);
1041        }
1042
1043        Err(anyhow!("payload must be of type MACPayload"))
1044    }
1045
1046    #[cfg(feature = "crypto")]
1047    fn calculate_upink_join_mic(&self, key: &AES128Key) -> Result<[u8; 4]> {
1048        // mic bytes
1049        let mut mic_bytes = Vec::new();
1050
1051        mic_bytes.extend_from_slice(&self.mhdr.to_le_bytes());
1052        mic_bytes.extend_from_slice(&self.payload.to_vec()?);
1053
1054        let mut mac = Cmac::<Aes128>::new_from_slice(&key.to_bytes()).unwrap();
1055        mac.update(&mic_bytes);
1056
1057        let hash = mac.finalize().into_bytes();
1058        if hash.len() < 4 {
1059            return Err(anyhow!("hash is less than 4 bytes"));
1060        }
1061
1062        let mut mic: [u8; 4] = [0; 4];
1063        mic.clone_from_slice(&hash[0..4]);
1064        Ok(mic)
1065    }
1066
1067    #[cfg(feature = "crypto")]
1068    fn calculate_downlink_join_mic(
1069        &self,
1070        join_req_type: JoinType,
1071        join_eui: &EUI64,
1072        dev_nonce: u16,
1073        key: &AES128Key,
1074    ) -> Result<[u8; 4]> {
1075        if let Payload::JoinAccept(pl) = &self.payload {
1076            let mut mic_bytes = Vec::new();
1077
1078            // LoRaWAN 1.1
1079            if pl.dl_settings.opt_neg {
1080                mic_bytes.push(match join_req_type {
1081                    JoinType::Join => 0xff,
1082                    JoinType::RejoinType0 => 0x00,
1083                    JoinType::RejoinType1 => 0x01,
1084                    JoinType::RejoinType2 => 0x02,
1085                });
1086
1087                mic_bytes.extend_from_slice(&join_eui.to_le_bytes());
1088                mic_bytes.extend_from_slice(&dev_nonce.to_le_bytes());
1089            }
1090
1091            mic_bytes.extend_from_slice(&self.mhdr.to_le_bytes());
1092
1093            // JoinNonce | NetID | DevAddr | DLSettings | RxDelay | CFList
1094            mic_bytes.extend_from_slice(&pl.to_vec()?);
1095
1096            let mut mac = Cmac::<Aes128>::new_from_slice(&key.to_bytes()).unwrap();
1097            mac.update(&mic_bytes);
1098
1099            let hash = mac.finalize().into_bytes();
1100            if hash.len() < 4 {
1101                return Err(anyhow!("hash is less than 4 bytes"));
1102            }
1103
1104            let mut mic: [u8; 4] = [0; 4];
1105            mic.clone_from_slice(&hash[0..4]);
1106            return Ok(mic);
1107        }
1108
1109        Err(anyhow!("payload must be of type JoinAcceptPayload"))
1110    }
1111}
1112
1113/// Encrypt f_opts mac-command data.
1114/// For uplink:
1115///   Set the a_fcnt_down to false and use the f_cnt_up as f_cnt.
1116/// For downlink if f_port is unset or equal to 0:
1117///   Set the a_fcnt_down to false and use the n_fcnt_down as f_cnt.
1118/// For downlink if f_port > 0:
1119///   Set the a_fcnt_down to true and use the a_f_cnt_down as f_cnt.
1120#[cfg(feature = "crypto")]
1121pub fn encrypt_f_opts(
1122    nwk_s_enc_key: &AES128Key,
1123    a_fcnt_down: bool,
1124    uplink: bool,
1125    devaddr: &DevAddr,
1126    f_cnt: u32,
1127    data: &[u8],
1128) -> Result<Vec<u8>> {
1129    use aes::cipher::KeyInit;
1130
1131    if data.len() > 15 {
1132        return Err(anyhow!("max size of f_opts is 15 bytes"));
1133    }
1134
1135    let key_bytes = nwk_s_enc_key.to_bytes();
1136    let key = GenericArray::from_slice(&key_bytes);
1137    let cipher = Aes128::new(key);
1138
1139    let mut a = vec![0; 16];
1140    a[0] = 0x01;
1141    if a_fcnt_down {
1142        a[4] = 0x02;
1143    } else {
1144        a[4] = 0x01;
1145    }
1146
1147    if !uplink {
1148        a[5] = 0x01;
1149    }
1150
1151    a[6..10].clone_from_slice(&devaddr.to_le_bytes());
1152    a[10..14].clone_from_slice(&f_cnt.to_le_bytes());
1153    a[15] = 0x01;
1154
1155    let block = Block::from_mut_slice(&mut a);
1156    cipher.encrypt_block(block);
1157
1158    let mut out = vec![0; data.len()];
1159    for i in 0..data.len() {
1160        out[i] = data[i] ^ block[i];
1161    }
1162
1163    Ok(out)
1164}
1165
1166/// Encrypt (and decrypt) the frm_payload.
1167/// Note that the same function is used for encryption and decryption.
1168#[cfg(feature = "crypto")]
1169pub fn encrypt_frm_payload(
1170    key: &AES128Key,
1171    uplink: bool,
1172    devaddr: &DevAddr,
1173    f_cnt: u32,
1174    data: &[u8],
1175) -> Result<Vec<u8>> {
1176    use aes::cipher::KeyInit;
1177
1178    let mut data = data.to_vec();
1179    let data_len = data.len();
1180
1181    // make pt length multiple of 16
1182    if data.len() % 16 != 0 {
1183        data.append(&mut vec![0; 16 - (data.len() % 16)]);
1184    }
1185
1186    let key_bytes = key.to_bytes();
1187    let key = GenericArray::from_slice(&key_bytes);
1188    let cipher = Aes128::new(key);
1189
1190    let mut a = vec![0; 16];
1191    a[0] = 0x01;
1192    if !uplink {
1193        a[5] = 0x01;
1194    }
1195
1196    a[6..10].clone_from_slice(&devaddr.to_le_bytes());
1197    a[10..14].clone_from_slice(&f_cnt.to_le_bytes());
1198
1199    for i in 0..(data.len() / 16) {
1200        a[15] = (i + 1) as u8;
1201
1202        let mut block = Block::clone_from_slice(&a);
1203        cipher.encrypt_block(&mut block);
1204
1205        for j in 0..16 {
1206            data[(i * 16) + j] ^= block[j];
1207        }
1208    }
1209
1210    Ok(data[0..data_len].to_vec())
1211}
1212
1213fn is_uplink(f_type: FType) -> bool {
1214    match f_type {
1215        FType::JoinRequest
1216        | FType::UnconfirmedDataUp
1217        | FType::ConfirmedDataUp
1218        | FType::RejoinRequest => true,
1219        FType::JoinAccept | FType::UnconfirmedDataDown | FType::ConfirmedDataDown => false,
1220        FType::Proprietary => false,
1221    }
1222}
1223
1224fn decode_frm_payload(pl: &mut MACPayload, uplink: bool, f_port: u8, b: Vec<u8>) -> Result<()> {
1225    if f_port == 0 {
1226        let mut macs = MACCommandSet::new(vec![MACCommand::Raw(b)]);
1227        macs.decode_from_raw(uplink)?;
1228        pl.frm_payload = Some(FRMPayload::MACCommandSet(macs));
1229    } else if f_port == LA_FPORT_RELAY && uplink {
1230        pl.frm_payload = Some(FRMPayload::ForwardUplinkReq(ForwardUplinkReq::from_slice(
1231            &b,
1232        )?));
1233    } else if f_port == LA_FPORT_RELAY && !uplink {
1234        pl.frm_payload = Some(FRMPayload::ForwardDownlinkReq(
1235            ForwardDownlinkReq::from_slice(&b)?,
1236        ));
1237    } else {
1238        pl.frm_payload = Some(FRMPayload::Raw(b));
1239    }
1240
1241    Ok(())
1242}
1243
1244#[cfg(test)]
1245mod tests {
1246    use std::str::FromStr;
1247
1248    use super::super::eui64::EUI64;
1249    use super::super::mhdr::Major;
1250    use super::super::payload::JoinRequestPayload;
1251    use super::*;
1252
1253    struct PhyPayloadTest {
1254        phy: PhyPayload,
1255        bytes: Vec<u8>,
1256    }
1257
1258    #[test]
1259    fn test_proprietary() {
1260        let tests = vec![
1261            PhyPayloadTest {
1262                phy: PhyPayload {
1263                    mhdr: MHDR {
1264                        f_type: FType::Proprietary,
1265                        major: Major::LoRaWANR1,
1266                    },
1267                    payload: Payload::Raw(vec![]),
1268                    mic: None,
1269                },
1270                bytes: vec![0xe0],
1271            },
1272            PhyPayloadTest {
1273                phy: PhyPayload {
1274                    mhdr: MHDR {
1275                        f_type: FType::Proprietary,
1276                        major: Major::LoRaWANR1,
1277                    },
1278                    payload: Payload::Raw(vec![0x01, 0x02, 0x03]),
1279                    mic: None,
1280                },
1281                bytes: vec![0xe0, 0x01, 0x02, 0x03],
1282            },
1283        ];
1284
1285        for tst in tests {
1286            assert_eq!(tst.bytes, tst.phy.to_vec().unwrap());
1287            assert_eq!(tst.phy, PhyPayload::from_slice(&tst.bytes).unwrap());
1288        }
1289    }
1290
1291    #[test]
1292    // No need to test all the different mtypes, this is handled by the Payload type.
1293    fn test_non_proprietary() {
1294        let tests = vec![PhyPayloadTest {
1295            phy: PhyPayload {
1296                mhdr: MHDR {
1297                    f_type: FType::JoinRequest,
1298                    major: Major::LoRaWANR1,
1299                },
1300                payload: Payload::JoinRequest(JoinRequestPayload {
1301                    join_eui: EUI64::from_str("0102030405060708").unwrap(),
1302                    dev_eui: EUI64::from_str("0807060504030201").unwrap(),
1303                    dev_nonce: 1024,
1304                }),
1305                mic: Some([0x01, 0x02, 0x03, 0x04]),
1306            },
1307            bytes: vec![
1308                0x00, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05,
1309                0x06, 0x07, 0x08, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04,
1310            ],
1311        }];
1312
1313        for tst in tests {
1314            assert_eq!(tst.bytes, tst.phy.to_vec().unwrap());
1315            assert_eq!(tst.phy, PhyPayload::from_slice(&tst.bytes).unwrap());
1316        }
1317    }
1318}