LoRa_Craft/README.md

132 lines
6.1 KiB
Markdown

# 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](https://github.com/rpp0): [link here](https://github.com/rpp0/gr-lora)
* 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](https://github.com/PentHertz/LoRa_Craft/blob/master/img/completeschema.png "Schema to receive LoRa signal")
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](https://github.com/PentHertz/LoRa_Craft/blob/master/img/frequencydet_zoomout_sf12bw125.png "Waterfall and FFT sinks")
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`:
```bash
$ 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:
```bash
$ 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:
```python
>>> 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](https://github.com/rpp0/python-loranode) as follows:
```python
>>> 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:
```python
>>> 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:
```python
>>> 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](https://github.com/BastilleResearch/gr-lora)
Feel free to contribute if you have cool scripts/tools to share :)!