#!/usr/bin/en python

from __future__ import print_function

#    LoRa PHYdecoder - parse LoRa PHY decoded by gr-lora
#    Copyright (C) 2020  Sebastien Dudek (@FlUxIuS) @Penthertz
#    Code base on LoRa Craft project @PentHertz from commit a5f0a9d65c5ddc584035d5f45b52763e3a03a55f


from layers import LoRa
from scapy.layers.inet import UDP
from scapy.sendrecv import sniff
#from scapy.packet import bind_layers, Ether
from scapy.all import *
from scapy.utils import wrpcap
from scapy.utils import rdpcap
from lutil.crypto import *
import argparse
from lutil.fonts import *
import code

# default keys
cur_NwSKey = "2B7E151628AED2A6ABF7158809CF4F3C"
cur_AppSKey = "2B7E151628AED2A6ABF7158809CF4F3C"

lpkt = b"" # last pkt

savepcap = None

def savePCAP(pkt):
    global savepcap
    wrpcap(savepcap, pkt, append=True)

def decodePHY(pkt):
    global lpkt, savepcap
    if pkt != lpkt:
        lpkt = pkt
        decoded = LoRa(pkt[UDP].load)
        direction = 1
        print ()
        if decoded.MType & 0b001 == 0b1:
            direction = 0
            print ("<"+"-"*30)
        if decoded.MType & 0b001 == 0b0:
            print ("-"*30+">")
        print (repr(decoded))
        if savepcap is not None:
            savePCAP(pkt)

        if cur_AppSKey is not None:
            print (Fore.WHITE+Style.BRIGHT + "Deciphered Payload: ", decryptFRMPayload_1_0(binascii.unhexlify(cur_AppSKey), bytes(decoded), direction=direction), Style.RESET_ALL)


def filterpkt(pkt, port):
    if pkt.haslayer(UDP):
        if pkt[UDP].dport == port and pkt != lpkt:
            return True
    return False

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
                description='Monitor and decode MAC PHY packets.')
    parser.add_argument('-p', '--port', dest='port', default=40868,
                        help='TAP PORT to listen on (default: UDP 40868)')
    parser.add_argument('-i', '--iface', dest='iface', default='lo',
                        help='Interface to monitor (default: local)')
    parser.add_argument('-v', '--version', dest='version', default='1.1',
                        help='LoRaWAN version (1.1 by default)')
    parser.add_argument('-o', '--output', dest='output', default=None,
                        help='PCAP output filename')
    parser.add_argument('-c', '--intercative', dest='interact', action='store_true',
                        help='Interactive mode')
    parser.add_argument('-n', '--NwSKey', dest='netskey', default=None,
                        help='NwSKey')
    parser.add_argument('-a', '--AppSKey', dest='appskey', default=None,
                        help='AppSKey')

    args = parser.parse_args()
    iface = args.iface
    port = int(args.port)

    cur_NwSKey = args.netskey
    cur_AppSKey = args.appskey

    LoRa.version = args.version # setup LoRaWAN version
    savepcap = args.output

    if args.interact is True:
        bind_layers(UDP, LoRa)
        code.interact(local=locals())
    else:
        sniff(prn=decodePHY,
            lfilter=lambda pkt: filterpkt(pkt, port),
            iface=iface)
