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}