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):
|
||||
# returns packet type (int) and deserialized data
|
||||
print(s[1:].decode())
|
||||
return s[0], eval(s[1:].decode())
|
||||
|
||||
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:
|
||||
def __init__(self):
|
||||
print("Initializing LoRa...")
|
||||
|
||||
self.sender_id = str(uuid.uuid4())
|
||||
|
||||
# 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.begin(freq=915, bw=125, power=22)
|
||||
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):
|
||||
print(f"LORA EVENT: {events}")
|
||||
#print(f"LORA EVENT: {events}")
|
||||
if events & SX1262.RX_DONE:
|
||||
msg, err = sx.recv()
|
||||
msg, err = self.radio.recv()
|
||||
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:
|
||||
print('TX done.')
|
||||
#print('TX done.')
|
||||
pass
|
||||
@@ -1,23 +1,28 @@
|
||||
from sx1262 import SX1262
|
||||
from _sx126x import *
|
||||
import time
|
||||
import crypt_random
|
||||
|
||||
from bluetooth_handler import BluetoothHandler
|
||||
from lora_handler import LoRaHandler
|
||||
|
||||
LORA_ENABLED = False
|
||||
LORA_ENABLED = True
|
||||
BLUETOOTH_ENABLED = False
|
||||
|
||||
|
||||
def main():
|
||||
bluetooth_handler = None
|
||||
if BLUETOOTH_ENABLED:
|
||||
from bluetooth_handler import BluetoothHandler
|
||||
bluetooth_handler = BluetoothHandler()
|
||||
|
||||
lora_handler = None
|
||||
if LORA_ENABLED:
|
||||
lora_handler = LoRaHandler()
|
||||
|
||||
print("Halting Pico...")
|
||||
print("Ready!")
|
||||
while True:
|
||||
pass
|
||||
lora_handler.advertise()
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
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