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 math
from Identity import Identity
from Transport import Transport
from Packet import Packet
import FPE
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
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 padding
class Destination:
KEYSIZE = Identity.KEYSIZE;
PADDINGSIZE= Identity.PADDINGSIZE;
KEYSIZE = FPE.Identity.KEYSIZE;
PADDINGSIZE= FPE.Identity.PADDINGSIZE;
# Constants
SINGLE = 0x01;
GROUP = 0x02;
PLAIN = 0x03;
types = [SINGLE, GROUP, PLAIN]
SINGLE = 0x00;
GROUP = 0x01;
PLAIN = 0x02;
LINK = 0x03;
types = [SINGLE, GROUP, PLAIN, LINK]
IN = 0x11;
OUT = 0x12;
@ -70,7 +71,7 @@ class Destination:
self.callback = None
Transport.registerDestination(self)
FPE.Transport.registerDestination(self)
def __str__(self):

View File

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

View File

@ -1,5 +1,6 @@
import base64
import math
import FPE
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
@ -51,8 +52,7 @@ class Identity:
self.hash = Identity.getHash(self.pub_bytes)
self.hexhash = self.hash.encode("hex_codec")
print("Identity keys created, private length is "+str(len(self.prv_bytes)))
print("Identity keys created, public length is "+str(len(self.pub_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)
def getPrivateKey(self):
return self.prv_bytes
@ -83,7 +83,7 @@ class Identity:
if self.prv != None:
chunksize = (Identity.KEYSIZE-Identity.PADDINGSIZE)/8
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 = "";
for chunk in range(chunks):
@ -92,7 +92,7 @@ class Identity:
if (chunk+1)*chunksize > 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(
plaintext[start:end],
@ -102,7 +102,7 @@ class Identity:
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
else:
raise KeyError("Encryption failed because identity does not hold a private key")
@ -110,7 +110,7 @@ class Identity:
def decrypt(self, ciphertext):
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
chunks = int(math.ceil(len(ciphertext)/(float(chunksize))))
@ -121,7 +121,7 @@ class Identity:
if (chunk+1)*chunksize > 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(
ciphertext[start:end],
@ -150,3 +150,5 @@ class Identity:
else:
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 Interface import Interface
from time import sleep
import sys
import serial
import threading
import FPE
class SerialInterface(Interface):
MAX_CHUNK = 32768
@ -17,6 +19,7 @@ class SerialInterface(Interface):
serial = None
def __init__(self, owner, port, speed, databits, parity, stopbits):
self.serial = None
self.owner = owner
self.port = port
self.speed = speed
@ -31,6 +34,7 @@ class SerialInterface(Interface):
self.parity = serial.PARITY_ODD
try:
FPE.log("Opening serial port "+self.port+"...")
self.serial = serial.Serial(
port = self.port,
baudrate = self.speed,
@ -43,16 +47,16 @@ class SerialInterface(Interface):
write_timeout = None,
dsrdtr = False,
)
print(self.serial.inter_byte_timeout)
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
#self.serial.open()
if self.serial.is_open:
thread = threading.Thread(target=self.readLoop)
thread.setDaemon(True)
thread.start()
sleep(0.5)
FPE.log("Serial port "+self.port+" is now open")
else:
raise IOError("Could not open serial port")
@ -62,10 +66,13 @@ class SerialInterface(Interface):
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):
#pass
while self.serial.is_open:
data = self.serial.read(size=self.owner.__class__.MTU)
if not data == "":

View File

@ -1,30 +1,91 @@
import struct
from Transport import *
import FPE
class Packet:
# Constants
MESSAGE = 0x00;
RESOURCE = 0x01;
LINKREQUEST = 0x02;
PROOF = 0x03;
types = [MESSAGE, RESOURCE, LINKREQUEST, PROOF]
def __init__(self, destination, data):
self.destination = destination
self.data = data
self.flags = 0x00
self.header = 0x00
self.raw = None
self.sent = False
self.mtu = 0
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.transport_id = transport_id
self.data = data
self.flags = self.getPackedFlags()
self.raw = None
self.packed = False
self.sent = False
self.MTU = self.destination.MTU
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.ciphertext = self.destination.encrypt(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:
self.MTU = self.destination.MTU
self.header = struct.pack("!B", self.header ^ self.destination.type ^ self.flags)
self.header += self.destination.hash
self.ciphertext = self.destination.encrypt(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")
print("Size: "+str(len(self.raw)))
Transport.outbound(self.raw)
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
else:
raise IOError("Packet was already sent")

View File

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

View File

@ -1,11 +1,60 @@
import os
import glob
import time
from .Destination import Destination
from .FlexPE import FlexPE
from .Identity import Identity
from .Packet import Packet
from .Transport import Transport
from .Destination import Destination
from .Packet import Packet
modules = glob.glob(os.path.dirname(__file__)+"/*.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.setCallback(testCallback)
d2=Destination(identity, Destination.IN, Destination.PLAIN, "messenger", "user")
d2.setCallback(testCallback)
# d2=Destination(identity, Destination.IN, Destination.PLAIN, "plainchat", "markqvist")
# d2.setCallback(testCallback)
print identity.hexhash
print d1.name
print d1.hexhash
print d1.identity.pub
print "---"
print
#print identity.hexhash
#print d1.name
#print d1.hexhash
#print d1.identity.pub
#print "---"
#print
# p1=Packet(d1, "testmessage")
# p1.send()
msg=""
for x in range(300):
msg += "a"
signed = d1.sign(msg)
sl = len(signed)
pl = len(d1.identity.pub_bytes)
print("Signature length is "+str(sl))
print("Minimum announce is "+str(pl+sl+8))
#print("Signature length is "+str(sl))
#print("Minimum announce is "+str(pl+sl+8))
p1=Packet(d1, msg)
p1.send()
p2=Packet(d1, msg)
p2.send()
# p2=Packet(d2, "something else")
# p2=Packet(d2,"Test af msg")
# p2.send()
raw_input()