Table of content

What is this?

The rules of the game are explained in my original post.

4th Challenge

Challenge

Write a parser for LoRaWAN PHYPayload of lorawan spec. v1.1 Join-Request uplinks. New to this spec is that the keys are not sent with the join request as was the case for v1.0.x

Size (bytes) 1 7..M 4
PHYPayload MHDR MACPayload MIC
Bit # 7..5 4..2 1..0
MHDRbits MType RFU Major
MType meaning
000 Join-request
001 Join-accept
010 Unconfirmed Data Up
011 Unconfirmed Data Down
100 Confirmed Data Up
101 Confirmed Data Down
110 Rejoin-request
111 Proprietary
Size (bytes) 8 8 2
Join-request JoinEUI DevEUI DevNonce

MIC Calculation

Calculated according to RFC4493

cmacJoin = aes128_cmac(NwkKey, MHDR | JoinEUI | DevEUI | DevNonce)
MICJoin = cmac[0..3] 

Solution

Checking the MIC was too much work, but the structure was parsed..

	use std::fmt;
	use byteorder::{BigEndian, ByteOrder};

	struct JoinRequest{
	    join_eui:u64,
	    dev_eui:u64,
	    dev_nonce:u16
	}

	impl fmt::Display for JoinRequest{
	    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
	        write!(f, "deveui:{:#8x},joineui:{:#8x},devnonce:{:#2x}", self.dev_eui, self.join_eui, self.dev_nonce)
	    }
	}

	enum MType{
	    JoinRequest{mhdr:u8,message:JoinRequest,mic:u32},
	    _JoinAccept(),
	    _UnconfirmedDataUp(),
	    _UnconfirmedDataDown(),
	    _ConfirmedDataUp(),
	    _ConfirmedDataDown(),
	    _RejoinRequest(),
	    _Proprietary()
	}

	impl MType{
	    fn get_major(&self) -> Option<u8> {
	        match self{
	            MType::JoinRequest{mhdr,..} => Some(3 & mhdr),
	            _ => {println!("not implemented!"); None}
	        }
	    }
	}

	impl fmt::Display for MType{
	    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
	        match self{
	            MType::JoinRequest{message, mic,.. } =>{
	                write!(f, "major = {:#02x}, mic = {:#04x}, joinrequest = {}",
	                       self.get_major().unwrap(), *mic, *message
	                )
	            }
	            _ => write!(f, "Error")
	        }
	    }
	}


	fn main() {

	    /*
	        Message with MHDR = 01,
	        join eui = 01,02,03,04,05,,06,07,08
	        dev eui = 01,02,03,04,05,06,07,08
	        dev nuance = 00,01
	        mic = 01,02,03,04
	     */

	    let message:Vec<u8> = vec![1,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,0,1,1,2,3,4];

	    let m=parse(&message);


	    match m {
	        None => println!("Failed to parse!"),
	        _ => {
	            println!("{}",m.unwrap())
	        }
	    }


	}


	fn parse(message:&Vec<u8>) -> Option<MType> {
	    let mhdr = message.get(0);

	    match mhdr.unwrap()>>5 {
	        0 => Some(MType::JoinRequest {
	            mhdr: *mhdr.unwrap(),
	            message: JoinRequest {
	                join_eui: BigEndian::read_u64(&message[1..9]),
	                dev_eui: BigEndian::read_u64(&message[9..17]),
	                dev_nonce: BigEndian::read_u16(&message[17..19])
	            },
	            mic: BigEndian::read_u32(&message[19..23])
	        }),
	        _ => None
	    }
	}
	

See source on github see running version on rust playground