added basic cryptography, advertising, and forwarding functionality to the LoRa handler. Also added a UUID class for generating unique IDs for each device.
also, i have 2 radios now! :D
This commit is contained in:
@@ -57,6 +57,7 @@ class BluetoothHandler:
|
|||||||
|
|
||||||
def deserialize_msg(self, s: bytes):
|
def deserialize_msg(self, s: bytes):
|
||||||
# returns packet type (int) and deserialized data
|
# returns packet type (int) and deserialized data
|
||||||
|
print(s[1:].decode())
|
||||||
return s[0], eval(s[1:].decode())
|
return s[0], eval(s[1:].decode())
|
||||||
|
|
||||||
def _get_mac_address(self):
|
def _get_mac_address(self):
|
||||||
|
|||||||
16
relay/connection.py
Normal file
16
relay/connection.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from crypt_random import secure_random
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Connection:
|
||||||
|
def load_public_constants(self):
|
||||||
|
with open("public_constants.txt", "r") as f:
|
||||||
|
self.P = int(f.readline(), 16)
|
||||||
|
self.G = int(f.readline(), 16)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.load_public_constants()
|
||||||
|
|
||||||
|
self.private_key = int.from_bytes(secure_random(1024), "big")
|
||||||
|
self.public_key = pow(self.G, self.private_key) % self.P
|
||||||
39
relay/crypt_random.py
Normal file
39
relay/crypt_random.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
from machine import ADC
|
||||||
|
import utime
|
||||||
|
import uhashlib
|
||||||
|
|
||||||
|
adc = ADC(26)
|
||||||
|
|
||||||
|
|
||||||
|
def get_entropy(samples: int = 512):
|
||||||
|
entropy = bytearray()
|
||||||
|
|
||||||
|
for _ in range(samples):
|
||||||
|
|
||||||
|
value = adc.read_u16()
|
||||||
|
|
||||||
|
mixed = (
|
||||||
|
value ^
|
||||||
|
(value >> 2) ^
|
||||||
|
(value >> 5) ^
|
||||||
|
utime.ticks_cpu()
|
||||||
|
) & 0xFF
|
||||||
|
|
||||||
|
entropy.append(mixed)
|
||||||
|
|
||||||
|
utime.sleep_us(5)
|
||||||
|
|
||||||
|
return entropy
|
||||||
|
|
||||||
|
def secure_random(num_bytes: int = 32):
|
||||||
|
entropy = get_entropy()
|
||||||
|
|
||||||
|
digest = uhashlib.sha256(entropy).digest()
|
||||||
|
|
||||||
|
output = bytearray()
|
||||||
|
|
||||||
|
while len(output) < num_bytes:
|
||||||
|
digest = uhashlib.sha256(digest).digest()
|
||||||
|
output.extend(digest)
|
||||||
|
|
||||||
|
return bytes(output[:num_bytes])
|
||||||
@@ -1,17 +1,124 @@
|
|||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
from sx1262 import SX1262
|
||||||
|
|
||||||
|
from machine import Pin
|
||||||
|
led = Pin(25, Pin.OUT)
|
||||||
|
|
||||||
|
class FixedDeque:
|
||||||
|
def __init__(self, maxlen):
|
||||||
|
self.maxlen = maxlen
|
||||||
|
self.data = []
|
||||||
|
|
||||||
|
def append(self, x):
|
||||||
|
if len(self.data) >= self.maxlen:
|
||||||
|
self.data.pop(0)
|
||||||
|
self.data.append(x)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.data)
|
||||||
|
|
||||||
|
class LoRaMessage:
|
||||||
|
def __init__(self, sender_id: str, recipient_id: str, payload: bytes):
|
||||||
|
self.sender_id = sender_id
|
||||||
|
self.recipient_id = recipient_id
|
||||||
|
self.payload = payload
|
||||||
|
self.message_id = str(uuid.uuid4())
|
||||||
|
self.lifetime = 7
|
||||||
|
|
||||||
|
def encode(self) -> bytes:
|
||||||
|
encoded = bytearray()
|
||||||
|
|
||||||
|
encoded.append(len(self.sender_id))
|
||||||
|
encoded.extend(self.sender_id.encode("utf-8"))
|
||||||
|
|
||||||
|
encoded.append(len(self.recipient_id))
|
||||||
|
encoded.extend(self.recipient_id.encode("utf-8"))
|
||||||
|
|
||||||
|
encoded.extend(self.payload)
|
||||||
|
|
||||||
|
return bytes(encoded)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def decode(data: bytes):
|
||||||
|
sender_id_len = data[0]
|
||||||
|
sender_id = data[1:1+sender_id_len].decode("utf-8")
|
||||||
|
|
||||||
|
recipient_id_len = data[1+sender_id_len]
|
||||||
|
recipient_id = data[2+sender_id_len:2+sender_id_len+recipient_id_len].decode("utf-8")
|
||||||
|
|
||||||
|
payload = data[2+sender_id_len+recipient_id_len:]
|
||||||
|
|
||||||
|
return LoRaMessage(sender_id=sender_id, recipient_id=recipient_id, payload=payload)
|
||||||
|
|
||||||
class LoRaHandler:
|
class LoRaHandler:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
print("Initializing LoRa...")
|
print("Initializing LoRa...")
|
||||||
|
|
||||||
|
self.sender_id = str(uuid.uuid4())
|
||||||
|
|
||||||
# initialize our radio, im using the HAT SX1262 hat for the pico
|
# initialize our radio, im using the HAT SX1262 hat for the pico
|
||||||
self.radio = SX1262(spi_bus=1, clk=10, mosi=11, miso=12, cs=3, irq=20, rst=15, gpio=2)
|
self.radio = SX1262(spi_bus=1, clk=10, mosi=11, miso=12, cs=3, irq=20, rst=15, gpio=2)
|
||||||
self.radio.begin(freq=915, bw=125, power=22)
|
self.radio.begin(freq=915, bw=125, power=22)
|
||||||
self.radio.setBlockingCallback(False, self.irq)
|
self.radio.setBlockingCallback(False, self.irq)
|
||||||
|
|
||||||
|
self.recent_messages = FixedDeque(32)
|
||||||
|
self.neighbours = set()
|
||||||
|
|
||||||
|
def send(self, data: bytes, recipient_id: str):
|
||||||
|
msg_bytes = LoRaMessage(sender_id=self.sender_id, recipient_id=recipient_id, payload=data).encode()
|
||||||
|
print(msg_bytes)
|
||||||
|
self.radio.send(msg_bytes)
|
||||||
|
|
||||||
|
def forward(self, msg: LoRaMessage):
|
||||||
|
msg_bytes = msg.encode()
|
||||||
|
self.radio.send(msg_bytes)
|
||||||
|
|
||||||
|
def advertise(self):
|
||||||
|
# send an advertisement message to let neighbors know we're here
|
||||||
|
self.send(b"ADVERTISE", recipient_id="BROADCAST")
|
||||||
|
|
||||||
|
def on_receive(self, msg: LoRaMessage):
|
||||||
|
# ensure we don't process a message twice
|
||||||
|
if msg.message_id in self.recent_messages:
|
||||||
|
return
|
||||||
|
self.recent_messages.append(msg.message_id)
|
||||||
|
|
||||||
|
# messages has a lifetime to prevent infinite loops.
|
||||||
|
# if we receive a message with lifetime <= 0 we just drop it,
|
||||||
|
# otherwise we decrement the lifetime and forward it to all
|
||||||
|
# neighbors except the sender
|
||||||
|
if msg.lifetime <= 0:
|
||||||
|
return
|
||||||
|
msg.lifetime -= 1
|
||||||
|
|
||||||
|
# forward messages if they aren't meant for us
|
||||||
|
if (msg.recipient_id != self.sender_id) and msg.recipient_id != "BROADCAST":
|
||||||
|
print("Forwarding message...")
|
||||||
|
self.forward(msg)
|
||||||
|
return
|
||||||
|
|
||||||
|
if msg.payload == b"ADVERTISE":
|
||||||
|
if not msg.sender_id in self.neighbours:
|
||||||
|
print(f"Neighbour discovered: {msg.sender_id}")
|
||||||
|
self.neighbours.add(msg.sender_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Received message from {msg.sender_id}: {msg.payload}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def irq(self, events):
|
def irq(self, events):
|
||||||
print(f"LORA EVENT: {events}")
|
#print(f"LORA EVENT: {events}")
|
||||||
if events & SX1262.RX_DONE:
|
if events & SX1262.RX_DONE:
|
||||||
msg, err = sx.recv()
|
msg, err = self.radio.recv()
|
||||||
error = SX1262.STATUS[err]
|
error = SX1262.STATUS[err]
|
||||||
print('Receive: {}, {}'.format(msg, error))
|
|
||||||
|
if error != "ERR_NONE":
|
||||||
|
print(f"Error receiving message: {error}")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.on_receive(LoRaMessage.decode(msg))
|
||||||
elif events & SX1262.TX_DONE:
|
elif events & SX1262.TX_DONE:
|
||||||
print('TX done.')
|
#print('TX done.')
|
||||||
|
pass
|
||||||
@@ -1,23 +1,28 @@
|
|||||||
from sx1262 import SX1262
|
from sx1262 import SX1262
|
||||||
from _sx126x import *
|
from _sx126x import *
|
||||||
import time
|
import time
|
||||||
|
import crypt_random
|
||||||
|
|
||||||
from bluetooth_handler import BluetoothHandler
|
|
||||||
from lora_handler import LoRaHandler
|
from lora_handler import LoRaHandler
|
||||||
|
|
||||||
LORA_ENABLED = False
|
LORA_ENABLED = True
|
||||||
|
BLUETOOTH_ENABLED = False
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
bluetooth_handler = None
|
||||||
|
if BLUETOOTH_ENABLED:
|
||||||
|
from bluetooth_handler import BluetoothHandler
|
||||||
bluetooth_handler = BluetoothHandler()
|
bluetooth_handler = BluetoothHandler()
|
||||||
|
|
||||||
lora_handler = None
|
lora_handler = None
|
||||||
if LORA_ENABLED:
|
if LORA_ENABLED:
|
||||||
lora_handler = LoRaHandler()
|
lora_handler = LoRaHandler()
|
||||||
|
|
||||||
print("Halting Pico...")
|
print("Ready!")
|
||||||
while True:
|
while True:
|
||||||
pass
|
lora_handler.advertise()
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
2
relay/public_constants.txt
Normal file
2
relay/public_constants.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF
|
||||||
|
2
|
||||||
22
relay/uuid.py
Normal file
22
relay/uuid.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import urandom
|
||||||
|
|
||||||
|
def uuid4():
|
||||||
|
b = bytearray(urandom.getrandbits(8) for _ in range(16))
|
||||||
|
|
||||||
|
# Set version (4) -> bits 12-15 of time_hi_and_version
|
||||||
|
b[6] = (b[6] & 0x0F) | 0x40
|
||||||
|
|
||||||
|
# Set variant (RFC 4122)
|
||||||
|
b[8] = (b[8] & 0x3F) | 0x80
|
||||||
|
|
||||||
|
def to_hex(x):
|
||||||
|
return '{:02x}'.format(x)
|
||||||
|
|
||||||
|
return (
|
||||||
|
to_hex(b[0]) + to_hex(b[1]) + to_hex(b[2]) + to_hex(b[3]) + '-' +
|
||||||
|
to_hex(b[4]) + to_hex(b[5]) + '-' +
|
||||||
|
to_hex(b[6]) + to_hex(b[7]) + '-' +
|
||||||
|
to_hex(b[8]) + to_hex(b[9]) + '-' +
|
||||||
|
to_hex(b[10]) + to_hex(b[11]) + to_hex(b[12]) +
|
||||||
|
to_hex(b[13]) + to_hex(b[14]) + to_hex(b[15])
|
||||||
|
)
|
||||||
46
test.py
46
test.py
@@ -1,46 +0,0 @@
|
|||||||
from bleak import BleakScanner, BleakClient
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
devices = await BleakScanner.discover(service_uuids=["E1898FF7-5063-4441-a6eb-526073B00001"])
|
|
||||||
for device in devices:
|
|
||||||
print()
|
|
||||||
print(f"Name: {device.name}")
|
|
||||||
print(f"Address: {device.address}")
|
|
||||||
print(f"Details: {device.details}")
|
|
||||||
|
|
||||||
for device in devices:
|
|
||||||
try:
|
|
||||||
this_device = await BleakScanner.find_device_by_address(device.address, timeout=20)
|
|
||||||
async with BleakClient(this_device) as client:
|
|
||||||
print(f'Services found for device')
|
|
||||||
print(f'\tDevice address:{device.address}')
|
|
||||||
print(f'\tDevice name:{device.name}')
|
|
||||||
|
|
||||||
client.write_gatt_char()
|
|
||||||
|
|
||||||
print('\tServices:')
|
|
||||||
for service in client.services:
|
|
||||||
print()
|
|
||||||
print(f'\t\tDescription: {service.description}')
|
|
||||||
print(f'\t\tService: {service}')
|
|
||||||
|
|
||||||
print('\t\tCharacteristics:')
|
|
||||||
for c in service.characteristics:
|
|
||||||
print()
|
|
||||||
print(f'\t\t\tUUID: {c.uuid}'),
|
|
||||||
print(f'\t\t\tDescription: {c.uuid}')
|
|
||||||
print(f'\t\t\tHandle: {c.uuid}'),
|
|
||||||
print(f'\t\t\tProperties: {c.uuid}')
|
|
||||||
|
|
||||||
print('\t\tDescriptors:')
|
|
||||||
for descrip in c.descriptors:
|
|
||||||
print(f'\t\t\t{descrip}')
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Could not connect to device with info: {device}")
|
|
||||||
print(f"Error: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(main())
|
|
||||||
Reference in New Issue
Block a user