def encrypt(self, key, dir): """Encrypt FRMPayload Args: key (int): AES encryption key - device NwkSKey or AppSkey """ if self.payload.frmpayload == None: return plen = len(self.payload.frmpayload) if plen == 0: return k = int(math.ceil(plen / 16.0)) # Create the concatenated block S S = '' for i in range(k): Ai = struct.pack('<BLBLLBB', 1, 0, dir, self.payload.fhdr.devaddr, self.payload.fhdr.fcnt, 0, i + 1) S += aesEncrypt(intPackBytes(key, 16), Ai) # Pad frmpayload to a byte multiple of 16 pad = k * 16 - plen ppl = self.payload.frmpayload + intPackBytes(0, pad) # Perform the XOR function over the data: unpack 8 bytes # to long long ints, XOR and repack pld = '' for i in range(k): s = struct.unpack('Q', S[i * 8:(i + 1) * 8])[0] p = struct.unpack('Q', ppl[i * 8:(i + 1) * 8])[0] pld += struct.pack('Q', s ^ p) # Truncate the result to the original frmpayload length self.payload.frmpayload = pld[:plen]
def encode(self): """Create a binary representation of JoinAcceptMessage object. Returns: Packed JoinAccept message. """ # Encoding Join-accept: # MAC Header # 3 bytes appnonce # 3 bytes netid # 4 bytes devaddr # 1 byte dlsettings # 1 byte rxdelay # Optional cflist # Create the message header = self.mhdr.encode() msg = intPackBytes(self.appnonce, 3, endian='little') + \ intPackBytes(self.netid, 3, endian='little') + \ struct.pack('<L', self.devaddr) + \ struct.pack('B', self.dlsettings) + \ struct.pack('B', self.rxdelay) # CFList is not used in a Join Accept message for US/AU bands if self.cflist: pass # Create the MIC over the entire message self.mic = aesEncrypt(intPackBytes(self.appkey, 16), header + msg, mode='CMAC')[0:4] msg += self.mic # Add the header and encrypt the message using AES-128 decrypt data = header + aesDecrypt(intPackBytes(self.appkey, 16), msg) return data
def checkMIC(self, appkey): # MIC is calculated over the binary join request message # excluding the MIC. Use the first four bytes of encrypted # data, convert from little endian data to int. data = self.mhdr.encode() + struct.pack('<QQH', self.appeui, self.deveui, self.devnonce) aesdata = aesEncrypt(intPackBytes(appkey, 16), data, mode='CMAC') mic = struct.unpack('<L', aesdata[:4])[0] return mic == self.mic
def checkMIC(self, key): """Check the message integrity code Args: key (int): NwkSkey Returns: True on success, False otherwise """ # Calculate the MIC for this message using key msg = self.mhdr.encode() + self.payload.encode() B0 = struct.pack('<BLBLLBB', int('0x49', 16), 0, 0, self.payload.fhdr.devaddr, self.payload.fhdr.fcnt, 0, len(msg)) data = B0 + msg aesdata = aesEncrypt(intPackBytes(key, 16), data, mode='CMAC') mic = struct.unpack('<L', aesdata[:4])[0] # Compare to message MIC return mic == self.mic
def _createSessionKey(self, pre, app, msg): """Create a NwkSKey or AppSKey Creates the session keys NwkSKey and AppSKey specific for an end-device to encrypt and verify network communication and application data. Args: pre (int): 0x01 ofr NwkSKey, 0x02 for AppSKey app (Application): The applicaiton object. msg (JoinRequestMessage): The MAC Join Request message. Returns: int: 128 bit session key """ # Session key data: 0x0n | appnonce | netid | devnonce | pad (16) data = struct.pack('B', pre) + \ intPackBytes(app.appnonce, 3, endian='little') + \ intPackBytes(self.config.netid, 3, endian='little') + \ struct.pack('<H', msg.devnonce) + intPackBytes(0, 7) aesdata = aesEncrypt(intPackBytes(app.appkey, 16), data) key = intUnpackBytes(aesdata) return key
def encode(self): """Create a binary representation of MACMessage object. Returns: String of packed data. """ # Calculate the MIC. # The MIC is calculated as cmac = aes128_cmac(NwkSKey, B0 | msg) # MIC = cmac[0:3] # msg is defined as: MHDR | FHDR | FPort | FRMPayload # B0 is defined as: # 1 byte 0x49 | 4 bytes 0x00 | 1 byte dir=0 for uplink, 1 for downlink # 4 bytes devaddr | 4 bytes fcntup or fcntdown # 1 byte 0x00 | 1 bytes len msg = self.mhdr.encode() + self.payload.encode() B0 = struct.pack('<BLBLLBB', int('0x49', 16), 0, 1, self.devaddr, self.payload.fhdr.fcnt, 0, len(msg)) data = B0 + msg # Create the MIC over the entire message self.mic = aesEncrypt(intPackBytes(self.key, 16), data, mode='CMAC')[0:4] msg += self.mic return msg