LoRa_Craft/README.md

132 lines
6.1 KiB
Markdown
Raw Permalink Normal View History

2020-01-15 01:24:15 -08:00
# LoRa Craft
2020-01-20 01:22:19 -08:00
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.
2020-01-15 01:24:15 -08:00
2020-01-20 01:22:19 -08:00
This repository will be completed with other tools soon, depending on needs during assessments :)
2020-01-15 01:24:15 -08:00
## 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)
2020-01-20 01:22:19 -08:00
* Software-Defined Radio equipment (USRP, bladeRF, RTL-SDR dongle, etc.)
2020-01-15 01:24:15 -08:00
## Receive signal and decode its data
### Receive
2020-01-20 01:22:19 -08:00
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:
2020-01-15 01:24:15 -08:00
2020-01-15 04:16:45 -08:00
![alt text](https://github.com/PentHertz/LoRa_Craft/blob/master/img/completeschema.png "Schema to receive LoRa signal")
2020-01-15 01:24:15 -08:00
2020-01-20 01:22:19 -08:00
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:
2020-01-15 01:24:15 -08:00
2020-01-15 04:16:45 -08:00
![alt text](https://github.com/PentHertz/LoRa_Craft/blob/master/img/frequencydet_zoomout_sf12bw125.png "Waterfall and FFT sinks")
2020-01-15 01:24:15 -08:00
2020-01-20 01:22:19 -08:00
Note: Multiple frequencies can be used by targets. This would imply to include multiple receivers in GRC.
2020-01-15 01:24:15 -08:00
2020-01-15 01:31:56 -08:00
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.
2020-01-15 01:24:15 -08:00
### 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
2020-01-20 01:22:19 -08:00
To generate packets, you can instantiate a Scapy packet as follows:
2020-01-15 01:24:15 -08:00
```python
>>> from layers.loraphy2wan import *
2020-01-15 01:24:15 -08:00
>>> 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)
```
2020-01-20 01:22:19 -08:00
Note that you should skip the first three bytes (Preamble, PHDR, PHDR_CRC), before sending it with `send_p2p` method.
2020-01-15 01:24:15 -08:00
## 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 *
2020-01-15 01:24:15 -08:00
>>> 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))
2020-01-15 01:24:15 -08:00
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' |>>
2021-03-30 10:32:59 -07:00
>>> p = b"\x00\x00\x00\x20"+ja # adding headers
>>> checkMIC(binascii.unhexlify(key), p)
2020-01-15 01:24:15 -08:00
>>> 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 :)!