1use crate::crypto::Cipher;
4use crate::error::LorawanError;
5use crate::frame::{
6 Direction, PhyPayload, MAX_FRAME, MAX_PAYLOAD, MTYPE_CONFIRMED_DOWN, MTYPE_CONFIRMED_UP,
7 MTYPE_MASK, MTYPE_UNCONFIRMED_DOWN, MTYPE_UNCONFIRMED_UP,
8};
9
10const FHDR_LEN: usize = 8;
12const MIN_FRAME: usize = FHDR_LEN + 4;
14
15const FCTRL_ADR: u8 = 0x80;
18const FCTRL_ADR_ACK_REQ: u8 = 0x40;
19const FCTRL_ACK: u8 = 0x20;
20const FCTRL_FPENDING: u8 = 0x10;
21const FCTRL_CLASS_B: u8 = 0x10;
22const FCTRL_FOPTS_LEN: u8 = 0x0F;
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45pub struct Session {
46 dev_addr: u32,
47 nwk_skey: [u8; 16],
48 app_skey: [u8; 16],
49}
50
51impl Session {
52 pub fn new(dev_addr: u32, nwk_skey: [u8; 16], app_skey: [u8; 16]) -> Self {
64 Session {
65 dev_addr,
66 nwk_skey,
67 app_skey,
68 }
69 }
70
71 pub fn dev_addr(&self) -> u32 {
77 self.dev_addr
78 }
79
80 pub(crate) const fn keys(&self) -> ([u8; 16], [u8; 16]) {
82 (self.nwk_skey, self.app_skey)
83 }
84
85 pub fn root_wor_s_key(&self) -> [u8; 16] {
94 crate::relay::root_wor_s_key(&self.nwk_skey)
95 }
96
97 pub fn wor_keys(&self) -> crate::relay::WorKeys {
105 crate::relay::WorKeys::derive(&self.root_wor_s_key(), self.dev_addr)
106 }
107
108 pub fn encode_uplink(&self, uplink: &Uplink) -> Result<PhyPayload, LorawanError> {
124 let mtype = if uplink.confirmed {
125 MTYPE_CONFIRMED_UP
126 } else {
127 MTYPE_UNCONFIRMED_UP
128 };
129 let mut fctrl = 0;
130 if uplink.adr {
131 fctrl |= FCTRL_ADR;
132 }
133 if uplink.adr_ack_req {
134 fctrl |= FCTRL_ADR_ACK_REQ;
135 }
136 if uplink.ack {
137 fctrl |= FCTRL_ACK;
138 }
139 self.encode(
140 Direction::Uplink,
141 mtype,
142 fctrl,
143 uplink.fcnt,
144 uplink.fport,
145 uplink.fopts,
146 uplink.payload,
147 )
148 }
149
150 pub fn encode_downlink(&self, downlink: &Downlink) -> Result<PhyPayload, LorawanError> {
165 let mtype = if downlink.confirmed {
166 MTYPE_CONFIRMED_DOWN
167 } else {
168 MTYPE_UNCONFIRMED_DOWN
169 };
170 let mut fctrl = 0;
171 if downlink.adr {
172 fctrl |= FCTRL_ADR;
173 }
174 if downlink.ack {
175 fctrl |= FCTRL_ACK;
176 }
177 if downlink.fpending {
178 fctrl |= FCTRL_FPENDING;
179 }
180 self.encode(
181 Direction::Downlink,
182 mtype,
183 fctrl,
184 downlink.fcnt,
185 downlink.fport,
186 downlink.fopts,
187 downlink.payload,
188 )
189 }
190
191 #[allow(clippy::too_many_arguments)]
192 fn encode(
193 &self,
194 direction: Direction,
195 mtype: u8,
196 fctrl: u8,
197 fcnt: u32,
198 fport: Option<u8>,
199 fopts: &[u8],
200 payload: &[u8],
201 ) -> Result<PhyPayload, LorawanError> {
202 if fopts.len() > usize::from(FCTRL_FOPTS_LEN) {
203 return Err(LorawanError::PayloadTooLong);
204 }
205 if fport == Some(0) && !fopts.is_empty() {
207 return Err(LorawanError::MalformedFrame);
208 }
209 let len = MIN_FRAME + fopts.len() + usize::from(fport.is_some()) + payload.len();
210 if len > MAX_FRAME {
211 return Err(LorawanError::PayloadTooLong);
212 }
213
214 let mut buf = [0u8; MAX_FRAME];
215 buf[0] = mtype;
216 buf[1..5].copy_from_slice(&self.dev_addr.to_le_bytes());
217 buf[5] = fctrl | (fopts.len() as u8);
218 buf[6..8].copy_from_slice(&(fcnt as u16).to_le_bytes());
219 let mut at = FHDR_LEN;
220 buf[at..at + fopts.len()].copy_from_slice(fopts);
221 at += fopts.len();
222
223 if let Some(port) = fport {
226 buf[at] = port;
227 at += 1;
228
229 let key = self.payload_key(port);
230 crypt_payload(
231 key,
232 self.dev_addr,
233 direction,
234 fcnt,
235 payload,
236 &mut buf[at..at + payload.len()],
237 );
238 at += payload.len();
239 }
240
241 let mic = self.mic(direction, fcnt, &buf[..at]);
242 buf[at..at + 4].copy_from_slice(&mic);
243 at += 4;
244
245 PhyPayload::new(&buf[..at])
246 }
247
248 pub fn decode(&self, bytes: &[u8], fcnt: u32) -> Result<RxData, LorawanError> {
269 if bytes.len() < MIN_FRAME {
270 return Err(LorawanError::FrameTooShort);
271 }
272 let mtype = bytes[0] & MTYPE_MASK;
273 let (direction, confirmed) = match mtype {
274 MTYPE_UNCONFIRMED_UP => (Direction::Uplink, false),
275 MTYPE_CONFIRMED_UP => (Direction::Uplink, true),
276 MTYPE_UNCONFIRMED_DOWN => (Direction::Downlink, false),
277 MTYPE_CONFIRMED_DOWN => (Direction::Downlink, true),
278 other => return Err(LorawanError::UnsupportedMType(other)),
279 };
280
281 let dev_addr = u32::from_le_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]);
282 let fctrl = bytes[5];
283 let fopts_len = usize::from(fctrl & FCTRL_FOPTS_LEN);
284 let fcnt_low = u16::from_le_bytes([bytes[6], bytes[7]]);
285 if fcnt as u16 != fcnt_low {
286 return Err(LorawanError::FcntMismatch);
287 }
288
289 let mic_start = bytes.len() - 4;
290 let body_start = FHDR_LEN + fopts_len;
291 if mic_start < body_start {
292 return Err(LorawanError::FrameTooShort);
293 }
294 let expected = self.mic(direction, fcnt, &bytes[..mic_start]);
295 if bytes[mic_start..] != expected[..] {
296 return Err(LorawanError::MicMismatch);
297 }
298
299 if fopts_len > 0 && mic_start > body_start && bytes[body_start] == 0 {
300 return Err(LorawanError::MalformedFrame);
301 }
302
303 let mut fopts = [0u8; FCTRL_FOPTS_LEN as usize];
304 fopts[..fopts_len].copy_from_slice(&bytes[FHDR_LEN..FHDR_LEN + fopts_len]);
305
306 let mut payload = [0u8; MAX_PAYLOAD];
307 let (fport, payload_len) = if mic_start > body_start {
308 let fport = bytes[body_start];
309 let encrypted = &bytes[body_start + 1..mic_start];
310 let key = self.payload_key(fport);
311 crypt_payload(
312 key,
313 dev_addr,
314 direction,
315 fcnt,
316 encrypted,
317 &mut payload[..encrypted.len()],
318 );
319 (Some(fport), encrypted.len())
320 } else {
321 (None, 0)
322 };
323
324 let uplink = direction == Direction::Uplink;
325 Ok(RxData {
326 direction,
327 dev_addr,
328 fcnt_low,
329 confirmed,
330 adr: fctrl & FCTRL_ADR != 0,
331 adr_ack_req: uplink && fctrl & FCTRL_ADR_ACK_REQ != 0,
332 ack: fctrl & FCTRL_ACK != 0,
333 fpending: !uplink && fctrl & FCTRL_FPENDING != 0,
334 class_b: uplink && fctrl & FCTRL_CLASS_B != 0,
335 fport,
336 fopts,
337 fopts_len,
338 payload,
339 payload_len,
340 })
341 }
342
343 fn payload_key(&self, fport: u8) -> &[u8; 16] {
346 if fport == 0 {
347 &self.nwk_skey
348 } else {
349 &self.app_skey
350 }
351 }
352
353 fn mic(&self, direction: Direction, fcnt: u32, msg: &[u8]) -> [u8; 4] {
355 let mut block = [0u8; 16 + MAX_FRAME];
356 block[0] = 0x49;
357 block[5] = direction.bit();
358 block[6..10].copy_from_slice(&self.dev_addr.to_le_bytes());
359 block[10..14].copy_from_slice(&fcnt.to_le_bytes());
360 block[15] = msg.len() as u8;
361 block[16..16 + msg.len()].copy_from_slice(msg);
362 let tag = Cipher::new(&self.nwk_skey).cmac(&block[..16 + msg.len()]);
363 [tag[0], tag[1], tag[2], tag[3]]
364 }
365}
366
367fn crypt_payload(
370 key: &[u8; 16],
371 dev_addr: u32,
372 direction: Direction,
373 fcnt: u32,
374 input: &[u8],
375 output: &mut [u8],
376) {
377 let cipher = Cipher::new(key);
378 let blocks = input.len().div_ceil(16);
379 for i in 0..blocks {
380 let mut a = [0u8; 16];
381 a[0] = 0x01;
382 a[5] = direction.bit();
383 a[6..10].copy_from_slice(&dev_addr.to_le_bytes());
384 a[10..14].copy_from_slice(&fcnt.to_le_bytes());
385 a[15] = (i + 1) as u8;
386 let stream = cipher.encrypt_block(&a);
387
388 let start = i * 16;
389 let end = (start + 16).min(input.len());
390 for j in start..end {
391 output[j] = input[j] ^ stream[j - start];
392 }
393 }
394}
395
396#[derive(Clone, Copy, Debug)]
411pub struct Uplink<'a> {
412 fcnt: u32,
413 fport: Option<u8>,
414 payload: &'a [u8],
415 confirmed: bool,
416 adr: bool,
417 adr_ack_req: bool,
418 ack: bool,
419 fopts: &'a [u8],
420}
421
422impl<'a> Uplink<'a> {
423 pub fn new(fcnt: u32, fport: u8, payload: &'a [u8]) -> Self {
435 Uplink {
436 fcnt,
437 fport: Some(fport),
438 payload,
439 confirmed: false,
440 adr: false,
441 adr_ack_req: false,
442 ack: false,
443 fopts: &[],
444 }
445 }
446
447 pub fn empty(fcnt: u32) -> Self {
481 Uplink {
482 fport: None,
483 ..Uplink::new(fcnt, 0, &[])
484 }
485 }
486
487 pub fn confirmed(mut self) -> Self {
493 self.confirmed = true;
494 self
495 }
496
497 pub fn with_adr(mut self) -> Self {
503 self.adr = true;
504 self
505 }
506
507 pub fn with_adr_ack_req(mut self) -> Self {
516 self.adr_ack_req = true;
517 self
518 }
519
520 pub fn with_ack(mut self) -> Self {
526 self.ack = true;
527 self
528 }
529
530 pub fn with_fopts(mut self, fopts: &'a [u8]) -> Self {
540 self.fopts = fopts;
541 self
542 }
543}
544
545#[derive(Clone, Copy, Debug)]
550pub struct Downlink<'a> {
551 fcnt: u32,
552 fport: Option<u8>,
553 payload: &'a [u8],
554 confirmed: bool,
555 adr: bool,
556 ack: bool,
557 fpending: bool,
558 fopts: &'a [u8],
559}
560
561impl<'a> Downlink<'a> {
562 pub fn new(fcnt: u32, fport: u8, payload: &'a [u8]) -> Self {
574 Downlink {
575 fcnt,
576 fport: Some(fport),
577 payload,
578 confirmed: false,
579 adr: false,
580 ack: false,
581 fpending: false,
582 fopts: &[],
583 }
584 }
585
586 pub fn empty(fcnt: u32) -> Self {
600 Downlink {
601 fport: None,
602 ..Downlink::new(fcnt, 0, &[])
603 }
604 }
605
606 pub fn confirmed(mut self) -> Self {
612 self.confirmed = true;
613 self
614 }
615
616 pub fn with_adr(mut self) -> Self {
622 self.adr = true;
623 self
624 }
625
626 pub fn with_ack(mut self) -> Self {
632 self.ack = true;
633 self
634 }
635
636 pub fn with_fpending(mut self) -> Self {
642 self.fpending = true;
643 self
644 }
645
646 pub fn with_fopts(mut self, fopts: &'a [u8]) -> Self {
656 self.fopts = fopts;
657 self
658 }
659}
660
661#[derive(Clone, Copy, Debug, PartialEq, Eq)]
666pub struct RxData {
667 direction: Direction,
668 dev_addr: u32,
669 fcnt_low: u16,
670 confirmed: bool,
671 adr: bool,
672 adr_ack_req: bool,
673 ack: bool,
674 fpending: bool,
675 class_b: bool,
676 fport: Option<u8>,
677 fopts: [u8; FCTRL_FOPTS_LEN as usize],
678 fopts_len: usize,
679 payload: [u8; MAX_PAYLOAD],
680 payload_len: usize,
681}
682
683impl RxData {
684 pub fn direction(&self) -> Direction {
690 self.direction
691 }
692
693 pub fn dev_addr(&self) -> u32 {
699 self.dev_addr
700 }
701
702 pub fn fcnt(&self) -> u16 {
708 self.fcnt_low
709 }
710
711 pub fn confirmed(&self) -> bool {
717 self.confirmed
718 }
719
720 pub fn adr(&self) -> bool {
726 self.adr
727 }
728
729 pub fn adr_ack_req(&self) -> bool {
736 self.adr_ack_req
737 }
738
739 pub fn ack(&self) -> bool {
745 self.ack
746 }
747
748 pub fn fpending(&self) -> bool {
755 self.fpending
756 }
757
758 pub fn class_b(&self) -> bool {
764 self.class_b
765 }
766
767 pub fn fport(&self) -> Option<u8> {
773 self.fport
774 }
775
776 pub fn fopts(&self) -> &[u8] {
782 &self.fopts[..self.fopts_len]
783 }
784
785 pub fn payload(&self) -> &[u8] {
791 &self.payload[..self.payload_len]
792 }
793}
794
795#[cfg(test)]
796mod tests {
797 use super::*;
798
799 const NWK_SKEY: [u8; 16] = [0x01; 16];
800 const APP_SKEY: [u8; 16] = [0x02; 16];
801 const DEV_ADDR: u32 = 0x2601_1BDA;
802
803 fn session() -> Session {
804 Session::new(DEV_ADDR, NWK_SKEY, APP_SKEY)
805 }
806
807 #[test]
808 fn an_uplink_round_trips() {
809 let session = session();
810 let frame = session
811 .encode_uplink(&Uplink::new(10, 1, b"temperature"))
812 .unwrap();
813 let rx = session.decode(frame.as_bytes(), 10).unwrap();
814 assert_eq!(rx.direction(), Direction::Uplink);
815 assert_eq!(rx.dev_addr(), DEV_ADDR);
816 assert_eq!(rx.fcnt(), 10);
817 assert_eq!(rx.fport(), Some(1));
818 assert_eq!(rx.payload(), b"temperature");
819 assert!(!rx.confirmed());
820 }
821
822 #[test]
823 fn the_payload_is_encrypted_on_the_wire() {
824 let session = session();
825 let frame = session
826 .encode_uplink(&Uplink::new(1, 1, b"secret"))
827 .unwrap();
828 assert!(frame
830 .as_bytes()
831 .windows(b"secret".len())
832 .all(|window| window != b"secret"));
833 }
834
835 #[test]
836 fn the_header_is_laid_out_as_the_spec_requires() {
837 let session = session();
838 let frame = session
839 .encode_uplink(&Uplink::new(0x0102, 1, b"x"))
840 .unwrap();
841 let bytes = frame.as_bytes();
842 assert_eq!(bytes[0], MTYPE_UNCONFIRMED_UP);
843 assert_eq!(&bytes[1..5], &DEV_ADDR.to_le_bytes());
845 assert_eq!(&bytes[6..8], &0x0102u16.to_le_bytes());
847 }
848
849 #[test]
850 fn a_confirmed_downlink_round_trips_with_its_flags() {
851 let session = session();
852 let frame = session
853 .encode_downlink(&Downlink::new(5, 2, b"cmd").confirmed().with_fpending())
854 .unwrap();
855 let rx = session.decode(frame.as_bytes(), 5).unwrap();
856 assert_eq!(rx.direction(), Direction::Downlink);
857 assert!(rx.confirmed());
858 assert!(rx.fpending());
859 assert_eq!(rx.payload(), b"cmd");
860 }
861
862 #[test]
863 fn frame_options_round_trip() {
864 let session = session();
865 let frame = session
866 .encode_uplink(&Uplink::new(3, 1, b"d").with_fopts(&[0x02, 0x03]))
867 .unwrap();
868 let rx = session.decode(frame.as_bytes(), 3).unwrap();
869 assert_eq!(rx.fopts(), &[0x02, 0x03]);
870 assert_eq!(rx.payload(), b"d");
871 }
872
873 #[test]
874 fn an_empty_payload_round_trips() {
875 let session = session();
876 let frame = session.encode_uplink(&Uplink::new(1, 1, b"")).unwrap();
877 let rx = session.decode(frame.as_bytes(), 1).unwrap();
878 assert_eq!(rx.payload(), b"");
879 assert_eq!(rx.fport(), Some(1));
880 }
881
882 #[test]
883 fn a_tampered_payload_fails_the_mic() {
884 let session = session();
885 let frame = session.encode_uplink(&Uplink::new(1, 1, b"data")).unwrap();
886 let mut bytes = frame.as_bytes().to_vec();
887 let last = bytes.len() - 5; bytes[last] ^= 0xff;
889 assert_eq!(session.decode(&bytes, 1), Err(LorawanError::MicMismatch));
890 }
891
892 #[test]
893 fn the_wrong_counter_is_rejected() {
894 let session = session();
895 let frame = session.encode_uplink(&Uplink::new(7, 1, b"data")).unwrap();
896 assert_eq!(
897 session.decode(frame.as_bytes(), 8),
898 Err(LorawanError::FcntMismatch)
899 );
900 }
901
902 #[test]
903 fn a_join_frame_is_not_decoded_here() {
904 let session = session();
905 let bytes = [0u8; MIN_FRAME];
907 assert_eq!(
908 session.decode(&bytes, 0),
909 Err(LorawanError::UnsupportedMType(0x00))
910 );
911 }
912
913 #[test]
914 fn a_short_frame_is_rejected() {
915 let session = session();
916 assert_eq!(
917 session.decode(&[0x40, 0x00, 0x00], 0),
918 Err(LorawanError::FrameTooShort)
919 );
920 }
921
922 #[test]
923 fn port_zero_uses_the_network_key() {
924 let session = Session::new(DEV_ADDR, NWK_SKEY, APP_SKEY);
927 let frame = session.encode_uplink(&Uplink::new(1, 0, b"mac")).unwrap();
928 let other = Session::new(DEV_ADDR, NWK_SKEY, [0x33; 16]);
929 let rx = other.decode(frame.as_bytes(), 1).unwrap();
930 assert_eq!(rx.payload(), b"mac");
931 }
932
933 #[test]
934 fn the_largest_payload_round_trips() {
935 let session = session();
936 let payload = [0xAB; MAX_PAYLOAD];
937 let frame = session.encode_uplink(&Uplink::new(1, 1, &payload)).unwrap();
938 assert_eq!(frame.as_bytes().len(), crate::MAX_FRAME);
939 let rx = session.decode(frame.as_bytes(), 1).unwrap();
940 assert_eq!(rx.payload(), &payload[..]);
941 }
942
943 #[test]
944 fn the_full_frame_counter_is_bound_into_the_mic() {
945 let session = session();
946 let frame = session
949 .encode_uplink(&Uplink::new(0x0001_0001, 1, b"x"))
950 .unwrap();
951 assert_eq!(
953 session.decode(frame.as_bytes(), 0x0000_0001),
954 Err(LorawanError::MicMismatch)
955 );
956 let rx = session.decode(frame.as_bytes(), 0x0001_0001).unwrap();
958 assert_eq!(rx.fcnt(), 0x0001);
959 }
960
961 #[test]
962 fn another_sessions_keys_cannot_read_a_frame() {
963 let session = session();
964 let frame = session
965 .encode_uplink(&Uplink::new(1, 1, b"secret"))
966 .unwrap();
967 let stranger = Session::new(DEV_ADDR, [0xAA; 16], [0xBB; 16]);
968 assert_eq!(
969 stranger.decode(frame.as_bytes(), 1),
970 Err(LorawanError::MicMismatch)
971 );
972 }
973
974 fn resigned(
977 direction: Direction,
978 fcnt: u32,
979 frame: &[u8],
980 edit: impl Fn(&mut [u8]),
981 ) -> Vec<u8> {
982 let mut bytes = frame.to_vec();
983 edit(&mut bytes);
984 let at = bytes.len() - 4;
985 let mic = session().mic(direction, fcnt, &bytes[..at]);
986 bytes[at..].copy_from_slice(&mic);
987 bytes
988 }
989
990 #[test]
991 fn an_uplink_asking_for_an_answer_sets_bit_six() {
992 let session = session();
995 let frame = session
996 .encode_uplink(&Uplink::new(9, 1, b"x").with_adr().with_adr_ack_req())
997 .unwrap();
998 assert_eq!(frame.as_bytes()[5], 0xC0);
999
1000 let rx = session.decode(frame.as_bytes(), 9).unwrap();
1001 assert!(rx.adr());
1002 assert!(rx.adr_ack_req());
1003 assert!(!rx.class_b());
1004 assert!(!rx.fpending());
1005 }
1006
1007 #[test]
1008 fn a_downlink_reads_bit_six_as_reserved() {
1009 let frame = session()
1011 .encode_downlink(&Downlink::new(4, 2, b"x"))
1012 .unwrap();
1013 let bytes = resigned(Direction::Downlink, 4, frame.as_bytes(), |bytes| {
1014 bytes[5] |= 0x40;
1015 });
1016 let rx = session().decode(&bytes, 4).unwrap();
1017 assert!(!rx.adr_ack_req());
1018 }
1019
1020 #[test]
1021 fn bit_four_is_class_b_going_up_and_frame_pending_coming_down() {
1022 let frame = session().encode_uplink(&Uplink::new(2, 1, b"x")).unwrap();
1023 let bytes = resigned(Direction::Uplink, 2, frame.as_bytes(), |bytes| {
1024 bytes[5] |= 0x10;
1025 });
1026 let up = session().decode(&bytes, 2).unwrap();
1027 assert!(up.class_b());
1028 assert!(!up.fpending(), "an uplink has no pending bit to report");
1029
1030 let frame = session()
1031 .encode_downlink(&Downlink::new(2, 1, b"x").with_fpending())
1032 .unwrap();
1033 assert_eq!(frame.as_bytes()[5], 0x10);
1034 let down = session().decode(frame.as_bytes(), 2).unwrap();
1035 assert!(down.fpending());
1036 assert!(!down.class_b(), "a downlink has no class B bit to report");
1037 }
1038
1039 #[test]
1040 fn an_empty_frame_carries_no_port_byte() {
1041 let session = session();
1042 let answer = [0x06, 0xFF, 0x0A];
1043 let frame = session
1044 .encode_uplink(&Uplink::empty(5).with_fopts(&answer))
1045 .unwrap();
1046 assert_eq!(
1047 frame.as_bytes().len(),
1048 MIN_FRAME + answer.len(),
1049 "the header, the options and the MIC, with no port"
1050 );
1051
1052 let rx = session.decode(frame.as_bytes(), 5).unwrap();
1053 assert_eq!(rx.fport(), None);
1054 assert_eq!(rx.payload(), b"");
1055 assert_eq!(rx.fopts(), &answer);
1056
1057 let ack = session
1058 .encode_downlink(&Downlink::empty(1).with_ack())
1059 .unwrap();
1060 assert_eq!(
1061 ack.as_bytes().len(),
1062 MIN_FRAME,
1063 "an acknowledgment is all header"
1064 );
1065 let rx = session.decode(ack.as_bytes(), 1).unwrap();
1066 assert!(rx.ack());
1067 assert_eq!(rx.fport(), None);
1068 }
1069
1070 #[test]
1071 fn frame_options_on_port_zero_are_refused_both_ways() {
1072 let session = session();
1075 assert_eq!(
1076 session.encode_uplink(&Uplink::new(1, 0, b"mac").with_fopts(&[0x02])),
1077 Err(LorawanError::MalformedFrame)
1078 );
1079 assert_eq!(
1080 session.encode_downlink(&Downlink::new(1, 0, b"mac").with_fopts(&[0x02])),
1081 Err(LorawanError::MalformedFrame)
1082 );
1083
1084 let frame = session
1085 .encode_downlink(&Downlink::new(6, 1, b"mac").with_fopts(&[0x02]))
1086 .unwrap();
1087 let port = FHDR_LEN + 1;
1088 let bytes = resigned(Direction::Downlink, 6, frame.as_bytes(), |bytes| {
1089 bytes[port] = 0;
1090 });
1091 assert_eq!(session.decode(&bytes, 6), Err(LorawanError::MalformedFrame));
1092
1093 let mac = session.encode_uplink(&Uplink::new(1, 0, b"mac")).unwrap();
1095 assert!(session.decode(mac.as_bytes(), 1).is_ok());
1096 }
1097}