Restructuring and packet format

This commit is contained in:
Mark Qvist 2018-03-19 16:39:08 +01:00
parent 4c92493bc2
commit 3ea36ff9b3
10 changed files with 236 additions and 74 deletions

View File

@ -1,8 +1,7 @@
import base64 import base64
import math import math
from Identity import Identity import FPE
from Transport import Transport
from Packet import Packet
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
@ -10,15 +9,17 @@ from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.asymmetric import padding
class Destination: class Destination:
KEYSIZE = Identity.KEYSIZE; KEYSIZE = FPE.Identity.KEYSIZE;
PADDINGSIZE= Identity.PADDINGSIZE; PADDINGSIZE= FPE.Identity.PADDINGSIZE;
# Constants # Constants
SINGLE = 0x01; SINGLE = 0x00;
GROUP = 0x02; GROUP = 0x01;
PLAIN = 0x03; PLAIN = 0x02;
types = [SINGLE, GROUP, PLAIN] LINK = 0x03;
types = [SINGLE, GROUP, PLAIN, LINK]
IN = 0x11; IN = 0x11;
OUT = 0x12; OUT = 0x12;
@ -70,7 +71,7 @@ class Destination:
self.callback = None self.callback = None
Transport.registerDestination(self) FPE.Transport.registerDestination(self)
def __str__(self): def __str__(self):

View File

@ -1,4 +1,6 @@
from Interfaces import * from Interfaces import *
import FPE
import ConfigParser import ConfigParser
import jsonpickle import jsonpickle
from vendor.configobj import ConfigObj from vendor.configobj import ConfigObj
@ -25,8 +27,9 @@ class FlexPE:
if os.path.isfile(self.configpath): if os.path.isfile(self.configpath):
self.config = ConfigObj(self.configpath) self.config = ConfigObj(self.configpath)
FPE.log("Configuration loaded from "+self.configpath)
else: else:
print("Could not load config file, creating default configuration...") FPE.log("Could not load config file, creating default configuration...")
self.createDefaultConfig() self.createDefaultConfig()
self.applyConfig() self.applyConfig()
@ -39,23 +42,26 @@ class FlexPE:
@staticmethod @staticmethod
def incoming(data): def incoming(data):
packet = FPE.Packet(None, data)
header = struct.unpack("B", data[0]) packet.unpack()
hash = data[1:11]
type = header[0] & 0x03
for destination in FlexPE.destinations: for destination in FlexPE.destinations:
if destination.hash == hash and destination.type == type: if destination.hash == packet.destination_hash and destination.type == packet.destination_type:
destination.receive(data[11:]) destination.receive(packet.data)
@staticmethod @staticmethod
def outbound(raw): def outbound(raw):
for interface in FlexPE.interfaces: for interface in FlexPE.interfaces:
if interface.OUT: if interface.OUT:
FPE.log("Transmitting via: "+str(interface), FPE.LOG_DEBUG)
interface.processOutgoing(raw) interface.processOutgoing(raw)
def applyConfig(self): def applyConfig(self):
for option in self.config["logging"]:
value = self.config["logging"][option]
if option == "loglevel":
FPE.loglevel = int(value)
for name in self.config["interfaces"]: for name in self.config["interfaces"]:
c = self.config["interfaces"][name] c = self.config["interfaces"][name]
try: try:
@ -91,8 +97,8 @@ class FlexPE:
FlexPE.interfaces.append(interface) FlexPE.interfaces.append(interface)
except Exception as e: except Exception as e:
print("The interface \""+name+"\" could not be created. Check your configuration file for errors!") FPE.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", FPE.LOG_ERROR)
print("The contained error was: "+str(e)) FPE.log("The contained exception was: "+str(e), FPE.LOG_ERROR)

View File

@ -1,5 +1,6 @@
import base64 import base64
import math import math
import FPE
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
@ -51,8 +52,7 @@ class Identity:
self.hash = Identity.getHash(self.pub_bytes) self.hash = Identity.getHash(self.pub_bytes)
self.hexhash = self.hash.encode("hex_codec") self.hexhash = self.hash.encode("hex_codec")
print("Identity keys created, private length is "+str(len(self.prv_bytes))) FPE.log("Identity keys created, private length is "+str(len(self.prv_bytes))+" public length is "+str(len(self.pub_bytes)), FPE.LOG_INFO)
print("Identity keys created, public length is "+str(len(self.pub_bytes)))
def getPrivateKey(self): def getPrivateKey(self):
return self.prv_bytes return self.prv_bytes
@ -83,7 +83,7 @@ class Identity:
if self.prv != None: if self.prv != None:
chunksize = (Identity.KEYSIZE-Identity.PADDINGSIZE)/8 chunksize = (Identity.KEYSIZE-Identity.PADDINGSIZE)/8
chunks = int(math.ceil(len(plaintext)/(float(chunksize)))) chunks = int(math.ceil(len(plaintext)/(float(chunksize))))
print("Plaintext size is "+str(len(plaintext))+", with "+str(chunks)+" chunks") # TODO: Remove debug output print("Plaintext size is "+str(len(plaintext))+", with "+str(chunks)+" chunks")
ciphertext = ""; ciphertext = "";
for chunk in range(chunks): for chunk in range(chunks):
@ -92,7 +92,7 @@ class Identity:
if (chunk+1)*chunksize > len(plaintext): if (chunk+1)*chunksize > len(plaintext):
end = len(plaintext) end = len(plaintext)
print("Processing chunk "+str(chunk+1)+" of "+str(chunks)+". Starting at "+str(start)+" and stopping at "+str(end)+". The length is "+str(len(plaintext[start:end]))) # TODO: Remove debug output print("Processing chunk "+str(chunk+1)+" of "+str(chunks)+". Starting at "+str(start)+" and stopping at "+str(end)+". The length is "+str(len(plaintext[start:end])))
ciphertext += self.pub.encrypt( ciphertext += self.pub.encrypt(
plaintext[start:end], plaintext[start:end],
@ -102,7 +102,7 @@ class Identity:
label=None label=None
) )
) )
print("Plaintext encrypted, ciphertext length is "+str(len(ciphertext))+" bytes.") # TODO: Remove debug output print("Plaintext encrypted, ciphertext length is "+str(len(ciphertext))+" bytes.")
return ciphertext return ciphertext
else: else:
raise KeyError("Encryption failed because identity does not hold a private key") raise KeyError("Encryption failed because identity does not hold a private key")
@ -110,7 +110,7 @@ class Identity:
def decrypt(self, ciphertext): def decrypt(self, ciphertext):
if self.prv != None: if self.prv != None:
print("Ciphertext length is "+str(len(ciphertext))+". ") # TODO: Remove debug output print("Ciphertext length is "+str(len(ciphertext))+". ")
chunksize = (Identity.KEYSIZE)/8 chunksize = (Identity.KEYSIZE)/8
chunks = int(math.ceil(len(ciphertext)/(float(chunksize)))) chunks = int(math.ceil(len(ciphertext)/(float(chunksize))))
@ -121,7 +121,7 @@ class Identity:
if (chunk+1)*chunksize > len(ciphertext): if (chunk+1)*chunksize > len(ciphertext):
end = len(ciphertext) end = len(ciphertext)
print("Processing chunk "+str(chunk+1)+" of "+str(chunks)+". Starting at "+str(start)+" and stopping at "+str(end)+". The length is "+str(len(ciphertext[start:end]))) # TODO: Remove debug output print("Processing chunk "+str(chunk+1)+" of "+str(chunks)+". Starting at "+str(start)+" and stopping at "+str(end)+". The length is "+str(len(ciphertext[start:end])))
plaintext += self.prv.decrypt( plaintext += self.prv.decrypt(
ciphertext[start:end], ciphertext[start:end],
@ -150,3 +150,5 @@ class Identity:
else: else:
raise KeyError("Signing failed because identity does not hold a private key") raise KeyError("Signing failed because identity does not hold a private key")
def announce(self):
pass

View File

@ -1,8 +1,10 @@
from __future__ import print_function from __future__ import print_function
from Interface import Interface from Interface import Interface
from time import sleep
import sys import sys
import serial import serial
import threading import threading
import FPE
class SerialInterface(Interface): class SerialInterface(Interface):
MAX_CHUNK = 32768 MAX_CHUNK = 32768
@ -17,6 +19,7 @@ class SerialInterface(Interface):
serial = None serial = None
def __init__(self, owner, port, speed, databits, parity, stopbits): def __init__(self, owner, port, speed, databits, parity, stopbits):
self.serial = None
self.owner = owner self.owner = owner
self.port = port self.port = port
self.speed = speed self.speed = speed
@ -31,6 +34,7 @@ class SerialInterface(Interface):
self.parity = serial.PARITY_ODD self.parity = serial.PARITY_ODD
try: try:
FPE.log("Opening serial port "+self.port+"...")
self.serial = serial.Serial( self.serial = serial.Serial(
port = self.port, port = self.port,
baudrate = self.speed, baudrate = self.speed,
@ -43,16 +47,16 @@ class SerialInterface(Interface):
write_timeout = None, write_timeout = None,
dsrdtr = False, dsrdtr = False,
) )
print(self.serial.inter_byte_timeout)
except Exception as e: except Exception as e:
print("Could not create serial port", file=sys.stderr) FPE.log("Could not create serial port", FPE.LOG_ERROR)
raise e raise e
#self.serial.open()
if self.serial.is_open: if self.serial.is_open:
thread = threading.Thread(target=self.readLoop) thread = threading.Thread(target=self.readLoop)
thread.setDaemon(True) thread.setDaemon(True)
thread.start() thread.start()
sleep(0.5)
FPE.log("Serial port "+self.port+" is now open")
else: else:
raise IOError("Could not open serial port") raise IOError("Could not open serial port")
@ -62,10 +66,13 @@ class SerialInterface(Interface):
def processOutgoing(self,data): def processOutgoing(self,data):
self.serial.write(data) written = self.serial.write(data)
if written != len(data):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
def readLoop(self): def readLoop(self):
#pass
while self.serial.is_open: while self.serial.is_open:
data = self.serial.read(size=self.owner.__class__.MTU) data = self.serial.read(size=self.owner.__class__.MTU)
if not data == "": if not data == "":

View File

@ -1,21 +1,57 @@
import struct import struct
from Transport import * import FPE
class Packet: class Packet:
# Constants
MESSAGE = 0x00;
RESOURCE = 0x01;
LINKREQUEST = 0x02;
PROOF = 0x03;
types = [MESSAGE, RESOURCE, LINKREQUEST, PROOF]
def __init__(self, destination, data): 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]
def __init__(self, destination, data, packet_type = MESSAGE, transport_type = None, header_type = HEADER_1, transport_id = None):
if destination != None:
if transport_type == None:
transport_type = FPE.Transport.BROADCAST
self.header_type = header_type
self.packet_type = packet_type
self.transport_type = transport_type
self.hops = 0;
self.destination = destination self.destination = destination
self.transport_id = transport_id
self.data = data self.data = data
self.flags = 0x00 self.flags = self.getPackedFlags()
self.header = 0x00
self.raw = None
self.sent = False
self.mtu = 0
def send(self): self.raw = None
if not self.sent: self.packed = False
self.sent = False
self.MTU = self.destination.MTU self.MTU = self.destination.MTU
self.header = struct.pack("!B", self.header ^ self.destination.type ^ self.flags) else:
self.raw = data
self.packed = True
def getPackedFlags(self):
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")
self.header += self.destination.hash self.header += self.destination.hash
self.ciphertext = self.destination.encrypt(self.data) self.ciphertext = self.destination.encrypt(self.data)
self.raw = self.header + self.ciphertext self.raw = self.header + self.ciphertext
@ -23,8 +59,33 @@ class Packet:
if len(self.raw) > self.MTU: if len(self.raw) > self.MTU:
raise IOError("Packet size of "+str(len(self.raw))+" exceeds MTU of "+str(self.MTU)+" bytes") raise IOError("Packet size of "+str(len(self.raw))+" exceeds MTU of "+str(self.MTU)+" bytes")
print("Size: "+str(len(self.raw))) self.packed = True
Transport.outbound(self.raw)
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:
self.pack()
FPE.log("Size: "+str(len(self.raw))+" header is "+str(len(self.header))+" payload is "+str(len(self.ciphertext)), FPE.LOG_DEBUG)
FPE.Transport.outbound(self.raw)
self.sent = True self.sent = True
else: else:
raise IOError("Packet was already sent") raise IOError("Packet was already sent")

View File

@ -1,10 +1,17 @@
from FlexPE import FlexPE import FPE
class Transport: class Transport:
# Constants
BROADCAST = 0x00;
TRANSPORT = 0x01;
RELAY = 0x02;
TUNNEL = 0x03;
types = [BROADCAST, TRANSPORT, RELAY, TUNNEL]
@staticmethod @staticmethod
def outbound(raw): def outbound(raw):
FlexPE.outbound(raw) FPE.FlexPE.outbound(raw)
@staticmethod @staticmethod
def registerDestination(destination): def registerDestination(destination):
FlexPE.addDestination(destination) FPE.FlexPE.addDestination(destination)

View File

@ -1,11 +1,60 @@
import os import os
import glob import glob
import time
from .Destination import Destination
from .FlexPE import FlexPE from .FlexPE import FlexPE
from .Identity import Identity from .Identity import Identity
from .Packet import Packet
from .Transport import Transport from .Transport import Transport
from .Destination import Destination
from .Packet import Packet
modules = glob.glob(os.path.dirname(__file__)+"/*.py") modules = glob.glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')] __all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]
LOG_CRITICAL = 0
LOG_ERROR = 1
LOG_WARNING = 2
LOG_NOTICE = 3
LOG_INFO = 4
LOG_VERBOSE = 5
LOG_DEBUG = 6
LOG_STDOUT = 0x91
LOG_FILE = 0x92
loglevel = LOG_NOTICE
logfile = None
logdest = LOG_STDOUT
logtimefmt = "%Y-%m-%d %H:%M:%S"
def loglevelname(level):
if (level == LOG_CRITICAL):
return "Critical"
if (level == LOG_ERROR):
return "Error"
if (level == LOG_WARNING):
return "Warning"
if (level == LOG_NOTICE):
return "Notice"
if (level == LOG_INFO):
return "Info"
if (level == LOG_VERBOSE):
return "Verbose"
if (level == LOG_DEBUG):
return "Debug"
return "Unknown"
def log(msg, level=3):
# TODO: not thread safe
if loglevel >= level:
timestamp = time.time()
logstring = "["+time.strftime(logtimefmt)+"] ["+loglevelname(level)+"] "+msg
if (logdest == LOG_STDOUT):
print(logstring)
if (logdest == LOG_FILE and logfile != None):
file = open(logfile, "a")
file.write(logstring+"\n")
file.close()

30
Notes/Header format Normal file
View File

@ -0,0 +1,30 @@
header types
-----------------
type 1 00 One byte header, one 10 byte address field
type 2 01 One byte header, two 10 byte address fields
type 3 10 Reserved
type 4 11 Reserved
propagation types
-----------------
broadcast 00
transport 01
relay 10
tunnel 11
destination types
-----------------
single 00
group 01
plain 10
link 11
packet types
-----------------
message 00
resource 01
link request 10
proof 11

29
t.py
View File

@ -19,33 +19,32 @@ identity = Identity()
d1=Destination(identity, Destination.IN, Destination.SINGLE, "messenger", "user") d1=Destination(identity, Destination.IN, Destination.SINGLE, "messenger", "user")
d1.setCallback(testCallback) d1.setCallback(testCallback)
d2=Destination(identity, Destination.IN, Destination.PLAIN, "messenger", "user")
d2.setCallback(testCallback)
# d2=Destination(identity, Destination.IN, Destination.PLAIN, "plainchat", "markqvist") # d2=Destination(identity, Destination.IN, Destination.PLAIN, "plainchat", "markqvist")
# d2.setCallback(testCallback) # d2.setCallback(testCallback)
print identity.hexhash #print identity.hexhash
print d1.name #print d1.name
print d1.hexhash #print d1.hexhash
print d1.identity.pub #print d1.identity.pub
print "---" #print "---"
print #print
# p1=Packet(d1, "testmessage")
# p1.send()
msg="" msg=""
for x in range(300): for x in range(300):
msg += "a" msg += "a"
signed = d1.sign(msg) signed = d1.sign(msg)
sl = len(signed) sl = len(signed)
pl = len(d1.identity.pub_bytes) pl = len(d1.identity.pub_bytes)
print("Signature length is "+str(sl)) #print("Signature length is "+str(sl))
print("Minimum announce is "+str(pl+sl+8)) #print("Minimum announce is "+str(pl+sl+8))
p1=Packet(d1, msg)
p1.send()
p2=Packet(d1, msg) # p2=Packet(d2,"Test af msg")
p2.send()
# p2=Packet(d2, "something else")
# p2.send() # p2.send()
raw_input() raw_input()