Go to file
Sébastien Dudek 8fba4bba40 Fixing readme for Python3 and adding requirements 2023-03-10 01:40:37 +01:00
capture Release commit 2020-01-15 10:24:15 +01:00
grc_examples Release commit 2020-01-15 10:24:15 +01:00
img Release commit 2020-01-15 10:24:15 +01:00
layers Typo fix 2021-01-09 16:33:58 +01:00
lutil Fixing readme for Python3 and adding requirements 2023-03-10 01:40:37 +01:00
LICENSE Fixing License's file name 2021-03-30 19:37:15 +02:00
LoRa_PHYDecode.py Update LoRa_PHYDecode.py 2020-10-23 15:42:57 +08:00
README.md Fixing readme for Python3 and adding requirements 2023-03-10 01:40:37 +01:00
USRPB20xmini_LoRa_receive_UDP.py Release commit 2020-01-15 10:24:15 +01:00
requirements.txt Fixing readme for Python3 and adding requirements 2023-03-10 01:40:37 +01:00

README.md

LoRa Craft

LoRa Craft is a small set of tools to receive signals with SDR, decode et craft LoRaWAN packets on top of a gr-lora GNU Radio module.

This repository will be completed with other tools soon, depending on needs during assessments :)

Dependencies

  • Python 2 or 3
  • Scapy
  • GNU Radio 3.8
  • gr-lora from rpp0: link here
  • Software-Defined Radio equipment (USRP, bladeRF, RTL-SDR dongle, etc.)

Receive signal and decode its data

Receive

To receive a signal, an example of a GRC schema is available in the folder grc_examples/usrp_LoRa_decode_to_UDP.grc for USRP, as shown as follows:

alt text

The channel frequency, as well as the spreading factor and the bandwidth, must be set correctly to valid values with the help of the FFT and waterfall sinks:

alt text

Note: Multiple frequencies can be used by targets. This would imply to include multiple receivers in GRC.

For more information on how to detect LoRa signal, please take a look at the following post: https://penthertz.com/blog/testing-LoRa-with-SDR-and-handy-tools.html.

Decode

Once the receiver is running with the SDR equipment, we use the script LoRa_PHYDecode.py:

$ python LoRa_PHYDecode.py -h                                                                                                                                                                                1 ↵
usage: LoRa_PHYDecode.py [-h] [-p PORT] [-i IFACE]

Monitor and decode MAC PHY packets.

optional arguments:
  -h, --help            show this help message and exit
  -p PORT, --port PORT  TAP PORT to listen on (default: UDP 40868)
  -i IFACE, --iface IFACE
                        Interface to monitor (default: local)

By default, the script can be run as follows to decode received LoRa frames:

$ sudo python LoRa_PHYDecode.py
<LoRa  Preamble=0x1 PHDR=0x631e PHDR_CRC=0x0 MType=Unconfirmed Data Up RFU=0 Major=0 DevAddr=[<DevAddrElem  NwkID=0xad NwkAddr=0x600015 |>] FCtrl=[<FCtrl_UpLink  ADR=0 ADRACKReq=0 ACK=0 ClassB=0 FOptsLen=0 |>] FCnt=0 FPort=2 DataPayload='i\x06D\x94\x97\x08\xce!\xd9' MIC=0x4b516899 CRC=0x96e1 |>
...
<LoRa  Preamble=0x1 PHDR=0x631e PHDR_CRC=0x0 MType=Unconfirmed Data Up RFU=0 Major=0 DevAddr=[<DevAddrElem  NwkID=0xad NwkAddr=0x600015 |>] FCtrl=[<FCtrl_UpLink  ADR=0 ADRACKReq=0 ACK=0 ClassB=0 FOptsLen=0 |>] FCnt=0 FPort=2 DataPayload='penthertz' MIC=0x20a5fcba CRC=0xcdc |>
<LoRa  Preamble=0x0 PHDR=0xd30c PHDR_CRC=0x0 MType=Confirmed Data Up RFU=0 Major=0 DevAddr=[<DevAddrElem  NwkID=0xad NwkAddr=0x600015 |>] FCtrl=[<FCtrl_UpLink  ADR=0 ADRACKReq=0 ACK=0 ClassB=0 FOptsLen=1 |>] FCnt=0 FOpts_up=[<MACCommand_up  CID=LinkCheckReq LinkCheck=[''] |>] FOpts_down=[<MACCommand_down  CID=222 |>] FPort=92 DataPayload='' MIC=0x31c753f |>
<LoRa  Preamble=0x0 PHDR=0xd30c PHDR_CRC=0x0 MType=Confirmed Data Up RFU=0 Major=0 DevAddr=[<DevAddrElem  NwkID=0xad NwkAddr=0x600015 |>] FCtrl=[<FCtrl_UpLink  ADR=0 ADRACKReq=0 ACK=0 ClassB=0 FOptsLen=1 |>] FCnt=0 FOpts_up=[<MACCommand_up  CID=LinkCheckReq LinkCheck=[''] |>] FOpts_down=[<MACCommand_down  CID=222 |>] FPort=92 DataPayload='' MIC=0x31c753f |

Generate packets

To generate packets, you can instantiate a Scapy packet as follows:

>>> from layers.loraphy2wan import *
>>> pkt = LoRa()
>>> pkt
<LoRa  Join_Request_Field=[''] |>

And start to fill it.

After crafting your packet, you can use python-loranode as follows:

>>> from binascii
>>> from loranode import RN2483Controller
>>> to_send = binascii.hexlify(str(pkt))[3:]
>>> c = RN2483Controller("/dev/ttyACM0")  # Choose the correct /dev device here
>>> c.set_sf(7)  # choose your spreading factor here
>>> c.set_bw(150) # choose the bandwidth here
>>> c.set_cr("4/8")  # Set 4/8 coding for example
>>> c.send_p2p(to_send)

Note that you should skip the first three bytes (Preamble, PHDR, PHDR_CRC), before sending it with send_p2p method.

LoRa crypto helpers

Few helpers have been implemented to calculate MIC field, encrypt and decrypt packet:

  • JoinAcceptPayload_decrypt: decrypt Join-accept payloads;
  • JoinAcceptPayload_encrypt: encrypt Join-accept payloads;
  • getPHY_CMAC: compute MIC field of a packet using a provided key;
  • checkMIC: check MIC of a packet against a provided key.

As an example, to check if the key 000102030405060708090A0B0C0D0E0F is used to compute MIC on the following Join-request, we can write a little script as follows:

>>> from layers.loraphy2wan import *
>>> from lutil.crypto import *
>>> key = "000102030405060708090A0B0C0D0E0F"
>>> p = '000000006c6f7665636166656d656565746f6f00696953024c49'
>>> pkt = LoRa(binascii.unhexlify(p))
>>> pkt
<LoRa  Preamble=0x0 PHDR=0x0 PHDR_CRC=0x0 MType=Join-request RFU=0 Major=0 Join_Request_Field=[<Join_Request  AppEUI='lovecafe' DevEUI='meeetoo' DevNonce=26985 |>] MIC=0x53024c49 |>
>>> checkMIC(binascii.unhexlify(key), bytes(pkt))
True

To check if 000102030405060708090A0B0C0D0E0F key is used to encrypt a Join-accept message, we can combine JoinAcceptPayload_decrypt and checkMIC as follows:

>>> pkt = "000000200836e287a9805cb7ee9e5fff7c9ee97a"
>>> ja = JoinAcceptPayload_decrypt(binascii.unhexlify(key), binascii.unhexlify(pkt))
>>> ja
'ghi#\x01\x00\xb2\\C\x03\x00\x00{\x06O\x8a'
>>> Join_Accept(ja)
<Join_Accept  JoinAppNonce=0x6fe14a NetID=0x10203 DevAddr=0x68e8cb1 OptNeg=0 RX1DRoffset=0x0 RX2_Data_rate=0x0 RxDelay=0x0 |<Padding  load='\xbejsu' |>>
>>> p = b"\x00\x00\x00\x20"+ja # adding headers
>>> checkMIC(binascii.unhexlify(key), p)
>>> True

TODO

  • More helpers for other types of payloads
  • Implement helpers to transmit signal with dongles more easily
  • Transmit packets with SDR
  • Support gr-lora from Bastille: link here

Feel free to contribute if you have cool scripts/tools to share :)!