mirror of
https://github.com/markqvist/Reticulum.git
synced 2025-05-06 08:25:57 -04:00
Implemented resources
This commit is contained in:
parent
19d9b1a4a5
commit
de8d9cf722
10 changed files with 1891 additions and 183 deletions
348
RNS/Packet.py
348
RNS/Packet.py
|
@ -2,6 +2,198 @@ import struct
|
|||
import time
|
||||
import RNS
|
||||
|
||||
class Packet:
|
||||
# Constants
|
||||
DATA = 0x00
|
||||
ANNOUNCE = 0x01
|
||||
LINKREQUEST = 0x02
|
||||
PROOF = 0x03
|
||||
types = [DATA, ANNOUNCE, LINKREQUEST, PROOF]
|
||||
|
||||
HEADER_1 = 0x00 # Normal header format
|
||||
HEADER_2 = 0x01 # Header format used for link packets in transport
|
||||
HEADER_3 = 0x02 # Reserved
|
||||
HEADER_4 = 0x03 # Reserved
|
||||
header_types = [HEADER_1, HEADER_2, HEADER_3, HEADER_4]
|
||||
|
||||
# Context types
|
||||
NONE = 0x00
|
||||
RESOURCE = 0x01
|
||||
RESOURCE_ADV = 0x02
|
||||
RESOURCE_REQ = 0x03
|
||||
RESOURCE_HMU = 0x04
|
||||
RESOURCE_PRF = 0x05
|
||||
RESOURCE_ICL = 0x06
|
||||
RESOURCE_RCL = 0x07
|
||||
REQUEST = 0x08
|
||||
RESPONSE = 0x09
|
||||
COMMAND = 0x0A
|
||||
COMMAND_STAT = 0x0B
|
||||
LRPROOF = 0xFF
|
||||
|
||||
HEADER_MAXSIZE = 23
|
||||
|
||||
# Defaults
|
||||
TIMEOUT = 60
|
||||
|
||||
def __init__(self, destination, data, packet_type = DATA, context = NONE, transport_type = RNS.Transport.BROADCAST, header_type = HEADER_1, transport_id = None):
|
||||
if destination != None:
|
||||
if transport_type == None:
|
||||
transport_type = RNS.Transport.BROADCAST
|
||||
|
||||
self.header_type = header_type
|
||||
self.packet_type = packet_type
|
||||
self.transport_type = transport_type
|
||||
self.context = context
|
||||
|
||||
self.hops = 0;
|
||||
self.destination = destination
|
||||
self.transport_id = transport_id
|
||||
self.data = data
|
||||
self.flags = self.getPackedFlags()
|
||||
self.MTU = RNS.Reticulum.MTU
|
||||
|
||||
self.raw = None
|
||||
self.packed = False
|
||||
self.sent = False
|
||||
self.receipt = None
|
||||
self.fromPacked = False
|
||||
else:
|
||||
self.raw = data
|
||||
self.packed = True
|
||||
self.fromPacked = True
|
||||
|
||||
self.sent_at = None
|
||||
self.packet_hash = None
|
||||
|
||||
def getPackedFlags(self):
|
||||
if self.context == Packet.LRPROOF:
|
||||
packed_flags = (self.header_type << 6) | (self.transport_type << 4) | RNS.Destination.LINK | self.packet_type
|
||||
else:
|
||||
packed_flags = (self.header_type << 6) | (self.transport_type << 4) | (self.destination.type << 2) | self.packet_type
|
||||
return packed_flags
|
||||
|
||||
def pack(self):
|
||||
self.header = ""
|
||||
self.header += struct.pack("!B", self.flags)
|
||||
self.header += struct.pack("!B", self.hops)
|
||||
|
||||
|
||||
if self.context == Packet.LRPROOF:
|
||||
self.header += self.destination.link_id
|
||||
self.ciphertext = self.data
|
||||
else:
|
||||
if self.header_type == Packet.HEADER_1:
|
||||
self.header += self.destination.hash
|
||||
|
||||
if self.packet_type == Packet.ANNOUNCE:
|
||||
# Announce packets are not encrypted
|
||||
self.ciphertext = self.data
|
||||
elif self.packet_type == Packet.PROOF and self.context == Packet.RESOURCE_PRF:
|
||||
# Resource proofs are not encrypted
|
||||
self.ciphertext = self.data
|
||||
elif self.context == Packet.RESOURCE:
|
||||
# A resource takes care of symmetric
|
||||
# encryption by itself
|
||||
self.ciphertext = self.data
|
||||
else:
|
||||
# In all other cases, we encrypt the packet
|
||||
# with the destination's public key
|
||||
self.ciphertext = self.destination.encrypt(self.data)
|
||||
|
||||
if self.header_type == Packet.HEADER_2:
|
||||
if t_destination != None:
|
||||
self.header += self.t_destination
|
||||
else:
|
||||
raise IOError("Packet with header type 2 must have a transport ID")
|
||||
|
||||
|
||||
|
||||
|
||||
self.header += chr(self.context)
|
||||
|
||||
self.raw = self.header + self.ciphertext
|
||||
|
||||
if len(self.raw) > self.MTU:
|
||||
raise IOError("Packet size of "+str(len(self.raw))+" exceeds MTU of "+str(self.MTU)+" bytes")
|
||||
|
||||
self.packed = True
|
||||
|
||||
def unpack(self):
|
||||
self.flags = ord(self.raw[0])
|
||||
self.hops = ord(self.raw[1])
|
||||
|
||||
self.header_type = (self.flags & 0b11000000) >> 6
|
||||
self.transport_type = (self.flags & 0b00110000) >> 4
|
||||
self.destination_type = (self.flags & 0b00001100) >> 2
|
||||
self.packet_type = (self.flags & 0b00000011)
|
||||
|
||||
if self.header_type == Packet.HEADER_2:
|
||||
self.transport_id = self.raw[2:12]
|
||||
self.destination_hash = self.raw[12:22]
|
||||
self.context = ord(self.raw[22:23])
|
||||
self.data = self.raw[23:]
|
||||
else:
|
||||
self.transport_id = None
|
||||
self.destination_hash = self.raw[2:12]
|
||||
self.context = ord(self.raw[12:13])
|
||||
self.data = self.raw[13:]
|
||||
|
||||
self.packed = False
|
||||
|
||||
def send(self):
|
||||
if not self.sent:
|
||||
if not self.packed:
|
||||
self.pack()
|
||||
|
||||
if RNS.Transport.outbound(self):
|
||||
return self.receipt
|
||||
else:
|
||||
# TODO: Don't raise error here, handle gracefully
|
||||
raise IOError("Packet could not be sent! Do you have any outbound interfaces configured?")
|
||||
else:
|
||||
raise IOError("Packet was already sent")
|
||||
|
||||
def resend(self):
|
||||
if self.sent:
|
||||
Transport.outbound(self.raw)
|
||||
else:
|
||||
raise IOError("Packet was not sent yet")
|
||||
|
||||
def prove(self, destination=None):
|
||||
if self.fromPacked and self.destination:
|
||||
if self.destination.identity and self.destination.identity.prv:
|
||||
self.destination.identity.prove(self, destination)
|
||||
|
||||
# Generates a special destination that allows Reticulum
|
||||
# to direct the proof back to the proved packet's sender
|
||||
def generateProofDestination(self):
|
||||
return ProofDestination(self)
|
||||
|
||||
def validateProofPacket(self, proof_packet):
|
||||
return self.receipt.validateProofPacket(proof_packet)
|
||||
|
||||
def validateProof(self, proof):
|
||||
return self.receipt.validateProof(proof)
|
||||
|
||||
def updateHash(self):
|
||||
self.packet_hash = self.getHash()
|
||||
|
||||
def getHash(self):
|
||||
return RNS.Identity.fullHash(self.getHashablePart())
|
||||
|
||||
def getHashablePart(self):
|
||||
return self.raw[0:1]+self.raw[2:]
|
||||
|
||||
class ProofDestination:
|
||||
def __init__(self, packet):
|
||||
self.hash = packet.getHash()[:10];
|
||||
self.type = RNS.Destination.SINGLE
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
return plaintext
|
||||
|
||||
|
||||
class PacketReceipt:
|
||||
# Receipt status constants
|
||||
FAILED = 0x00
|
||||
|
@ -90,158 +282,4 @@ class PacketReceipt:
|
|||
class PacketReceiptCallbacks:
|
||||
def __init__(self):
|
||||
self.delivery = None
|
||||
self.timeout = None
|
||||
|
||||
class Packet:
|
||||
# Constants
|
||||
DATA = 0x00;
|
||||
ANNOUNCE = 0x01;
|
||||
LINKREQUEST = 0x02;
|
||||
PROOF = 0x03;
|
||||
types = [DATA, ANNOUNCE, LINKREQUEST, PROOF]
|
||||
|
||||
HEADER_1 = 0x00; # Normal header format
|
||||
HEADER_2 = 0x01; # Header format used for link packets in transport
|
||||
HEADER_3 = 0x02; # Normal header format, but used to indicate a link request proof
|
||||
HEADER_4 = 0x03; # Reserved
|
||||
header_types = [HEADER_1, HEADER_2, HEADER_3, HEADER_4]
|
||||
|
||||
# Defaults
|
||||
TIMEOUT = 3600.0
|
||||
|
||||
def __init__(self, destination, data, packet_type = DATA, transport_type = RNS.Transport.BROADCAST, header_type = HEADER_1, transport_id = None):
|
||||
if destination != None:
|
||||
if transport_type == None:
|
||||
transport_type = RNS.Transport.BROADCAST
|
||||
|
||||
self.header_type = header_type
|
||||
self.packet_type = packet_type
|
||||
self.transport_type = transport_type
|
||||
|
||||
self.hops = 0;
|
||||
self.destination = destination
|
||||
self.transport_id = transport_id
|
||||
self.data = data
|
||||
self.flags = self.getPackedFlags()
|
||||
self.MTU = RNS.Reticulum.MTU
|
||||
|
||||
self.raw = None
|
||||
self.packed = False
|
||||
self.sent = False
|
||||
self.receipt = None
|
||||
self.fromPacked = False
|
||||
else:
|
||||
self.raw = data
|
||||
self.packed = True
|
||||
self.fromPacked = True
|
||||
|
||||
self.sent_at = None
|
||||
self.packet_hash = None
|
||||
|
||||
def getPackedFlags(self):
|
||||
if self.header_type == Packet.HEADER_3:
|
||||
packed_flags = (self.header_type << 6) | (self.transport_type << 4) | RNS.Destination.LINK | self.packet_type
|
||||
else:
|
||||
packed_flags = (self.header_type << 6) | (self.transport_type << 4) | (self.destination.type << 2) | self.packet_type
|
||||
return packed_flags
|
||||
|
||||
def pack(self):
|
||||
self.header = ""
|
||||
self.header += struct.pack("!B", self.flags)
|
||||
self.header += struct.pack("!B", self.hops)
|
||||
if self.header_type == Packet.HEADER_2:
|
||||
if t_destination != None:
|
||||
self.header += self.t_destination
|
||||
else:
|
||||
raise IOError("Packet with header type 2 must have a transport ID")
|
||||
|
||||
if self.header_type == Packet.HEADER_1:
|
||||
self.header += self.destination.hash
|
||||
if self.packet_type != Packet.ANNOUNCE:
|
||||
self.ciphertext = self.destination.encrypt(self.data)
|
||||
else:
|
||||
self.ciphertext = self.data
|
||||
|
||||
if self.header_type == Packet.HEADER_3:
|
||||
self.header += self.destination.link_id
|
||||
self.ciphertext = self.data
|
||||
|
||||
self.raw = self.header + self.ciphertext
|
||||
|
||||
if len(self.raw) > self.MTU:
|
||||
raise IOError("Packet size of "+str(len(self.raw))+" exceeds MTU of "+str(self.MTU)+" bytes")
|
||||
|
||||
self.packed = True
|
||||
|
||||
def unpack(self):
|
||||
self.flags = ord(self.raw[0])
|
||||
self.hops = ord(self.raw[1])
|
||||
|
||||
self.header_type = (self.flags & 0b11000000) >> 6
|
||||
self.transport_type = (self.flags & 0b00110000) >> 4
|
||||
self.destination_type = (self.flags & 0b00001100) >> 2
|
||||
self.packet_type = (self.flags & 0b00000011)
|
||||
|
||||
if self.header_type == Packet.HEADER_2:
|
||||
self.transport_id = self.raw[2:12]
|
||||
self.destination_hash = self.raw[12:22]
|
||||
self.data = self.raw[22:]
|
||||
else:
|
||||
self.transport_id = None
|
||||
self.destination_hash = self.raw[2:12]
|
||||
self.data = self.raw[12:]
|
||||
|
||||
self.packed = False
|
||||
|
||||
def send(self):
|
||||
if not self.sent:
|
||||
if not self.packed:
|
||||
self.pack()
|
||||
|
||||
if RNS.Transport.outbound(self):
|
||||
return self.receipt
|
||||
else:
|
||||
# TODO: Don't raise error here, handle gracefully
|
||||
raise IOError("Packet could not be sent! Do you have any outbound interfaces configured?")
|
||||
else:
|
||||
raise IOError("Packet was already sent")
|
||||
|
||||
def resend(self):
|
||||
if self.sent:
|
||||
Transport.outbound(self.raw)
|
||||
else:
|
||||
raise IOError("Packet was not sent yet")
|
||||
|
||||
def prove(self, destination=None):
|
||||
if self.fromPacked and self.destination:
|
||||
if self.destination.identity and self.destination.identity.prv:
|
||||
self.destination.identity.prove(self, destination)
|
||||
|
||||
# Generates a special destination that allows Reticulum
|
||||
# to direct the proof back to the proved packet's sender
|
||||
def generateProofDestination(self):
|
||||
return ProofDestination(self)
|
||||
|
||||
def validateProofPacket(self, proof_packet):
|
||||
return self.receipt.validateProofPacket(proof_packet)
|
||||
|
||||
def validateProof(self, proof):
|
||||
return self.receipt.validateProof(proof)
|
||||
|
||||
def updateHash(self):
|
||||
self.packet_hash = self.getHash()
|
||||
|
||||
def getHash(self):
|
||||
return RNS.Identity.fullHash(self.getHashablePart())
|
||||
|
||||
def getHashablePart(self):
|
||||
return self.raw[0:1]+self.raw[2:]
|
||||
|
||||
class ProofDestination:
|
||||
def __init__(self, packet):
|
||||
self.hash = packet.getHash()[:10];
|
||||
self.type = RNS.Destination.SINGLE
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
return plaintext
|
||||
|
||||
self.timeout = None
|
Loading…
Add table
Add a link
Reference in a new issue