From 82744fb9edddf5a3f0d7a7f64ca9f84f06ac6609 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 19 Mar 2018 18:11:50 +0100 Subject: [PATCH] Announce and validation --- FPE/Destination.py | 16 +++++++++- FPE/FlexPE.py | 25 ++++++++++----- FPE/Identity.py | 77 +++++++++++++++++++++++++++++++++++++++++++--- FPE/Packet.py | 14 ++++++--- FPE/__init__.py | 10 ++++++ t.py | 18 ++--------- 6 files changed, 125 insertions(+), 35 deletions(-) diff --git a/FPE/Destination.py b/FPE/Destination.py index 29b8de4..e40e174 100755 --- a/FPE/Destination.py +++ b/FPE/Destination.py @@ -162,4 +162,18 @@ class Destination: # Creates an announce packet for this destination. # Application specific data can be added to the announce. def announce(self,app_data=None): - pass + destination_hash = self.hash + random_hash = self.identity.getRandomHash() + + signed_data = self.hash+self.identity.getPublicKey()+random_hash + if app_data != None: + signed_data += app_data + + signature = self.identity.sign(signed_data) + + announce_data = self.hash+self.identity.getPublicKey()+random_hash+signature + if app_data != None: + announce_data += app_data + + FPE.Packet(self, announce_data, FPE.Packet.ANNOUNCE).send() + diff --git a/FPE/FlexPE.py b/FPE/FlexPE.py index 4154dbd..f769d4c 100755 --- a/FPE/FlexPE.py +++ b/FPE/FlexPE.py @@ -1,6 +1,4 @@ from Interfaces import * -import FPE - import ConfigParser import jsonpickle from vendor.configobj import ConfigObj @@ -9,8 +7,10 @@ import array import os.path import os +import FPE + class FlexPE: - MTU = 600 + MTU = 500 router = None config = None destinations = [] @@ -18,6 +18,7 @@ class FlexPE: configdir = os.path.expanduser("~")+"/.flexpe" configpath = configdir+"/config" + packetlist = [] def __init__(self,config=None): if config != None: @@ -42,12 +43,20 @@ class FlexPE: @staticmethod def incoming(data): - packet = FPE.Packet(None, data) - packet.unpack() + packet_hash = FPE.Identity.fullHash(data) - for destination in FlexPE.destinations: - if destination.hash == packet.destination_hash and destination.type == packet.destination_type: - destination.receive(packet.data) + if not packet_hash in FlexPE.packetlist: + FlexPE.packetlist.append(packet_hash) + packet = FPE.Packet(None, data) + packet.unpack() + + if packet.packet_type == FPE.Packet.ANNOUNCE: + FPE.Identity.validateAnnounce(packet) + + if packet.packet_type == FPE.Packet.RESOURCE: + for destination in FlexPE.destinations: + if destination.hash == packet.destination_hash and destination.type == packet.destination_type: + destination.receive(packet.data) @staticmethod def outbound(raw): diff --git a/FPE/Identity.py b/FPE/Identity.py index e4875dc..f2a9960 100644 --- a/FPE/Identity.py +++ b/FPE/Identity.py @@ -1,19 +1,26 @@ import base64 import math +import os import FPE from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.serialization import load_der_public_key +from cryptography.hazmat.primitives.serialization import load_der_private_key from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric import padding class Identity: # Configure key size KEYSIZE = 1536; + DERKEYSIZE = 1808; # Padding size, not configurable PADDINGSIZE= 336; + # Storage + remembered_destinations = {} + def __init__(self): # Initialize keys to none self.prv = None @@ -26,12 +33,53 @@ class Identity: self.createKeys() @staticmethod - def getHash(pub_key): + def remember(hash, public_key, app_data = None): + FPE.log("Remembering "+FPE.hexrep(hash, False), FPE.LOG_VERBOSE) + Identity.remembered_destinations[hash] = [public_key, app_data] + + @staticmethod + def recall(identity): + pass + + @staticmethod + def fullHash(data): digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) - digest.update(pub_key) + digest.update(data) + + return digest.finalize() + + @staticmethod + def truncatedHash(data): + digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + digest.update(data) return digest.finalize()[:10] + @staticmethod + def validateAnnounce(packet): + if packet.packet_type == FPE.Packet.ANNOUNCE: + FPE.log("Validating announce from "+FPE.hexrep(packet.destination_hash), FPE.LOG_VERBOSE) + destination_hash = packet.destination_hash + public_key = packet.data[10:Identity.DERKEYSIZE/8+10] + random_hash = packet.data[Identity.DERKEYSIZE/8+10:Identity.DERKEYSIZE/8+20] + signature = packet.data[Identity.DERKEYSIZE/8+20:Identity.DERKEYSIZE/8+20+Identity.KEYSIZE/8] + app_data = "" + if len(packet.data) > Identity.DERKEYSIZE/8+20+Identity.KEYSIZE/8: + app_data = packet.data[Identity.DERKEYSIZE/8+20+Identity.KEYSIZE/8:] + + signed_data = destination_hash+public_key+random_hash+app_data + + announced_identity = Identity() + announced_identity.loadPublicKey(public_key) + + if announced_identity.validate(signature, signed_data): + FPE.log("Announce is valid", FPE.LOG_VERBOSE) + FPE.Identity.remember(destination_hash, public_key) + else: + FPE.log("Announce is invalid", FPE.LOG_VERBOSE) + + del announced_identity + def createKeys(self): self.prv = rsa.generate_private_key( public_exponent=65337, @@ -49,7 +97,7 @@ class Identity: format=serialization.PublicFormat.SubjectPublicKeyInfo ) - self.hash = Identity.getHash(self.pub_bytes) + self.hash = Identity.truncatedHash(self.pub_bytes) self.hexhash = self.hash.encode("hex_codec") FPE.log("Identity keys created, private length is "+str(len(self.prv_bytes))+" public length is "+str(len(self.pub_bytes)), FPE.LOG_INFO) @@ -150,5 +198,24 @@ class Identity: else: raise KeyError("Signing failed because identity does not hold a private key") - def announce(self): - pass \ No newline at end of file + def validate(self, signature, message): + if self.pub != None: + try: + self.pub.verify( + signature, + message, + padding.PSS( + mgf=padding.MGF1(hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH + ), + hashes.SHA256() + ) + return True + except: + return False + else: + raise KeyError("Signature validation failed because identity does not hold a public key") + + def getRandomHash(self): + return self.truncatedHash(os.urandom(10)) + diff --git a/FPE/Packet.py b/FPE/Packet.py index 767780b..58e3311 100755 --- a/FPE/Packet.py +++ b/FPE/Packet.py @@ -3,11 +3,11 @@ import FPE class Packet: # Constants - MESSAGE = 0x00; - RESOURCE = 0x01; + RESOURCE = 0x00; + ANNOUNCE = 0x01; LINKREQUEST = 0x02; PROOF = 0x03; - types = [MESSAGE, RESOURCE, LINKREQUEST, PROOF] + types = [RESOURCE, ANNOUNCE, LINKREQUEST, PROOF] HEADER_1 = 0x00; # Normal header format HEADER_2 = 0x01; # Header format used for link packets in transport @@ -15,7 +15,7 @@ class Packet: 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): + def __init__(self, destination, data, packet_type = RESOURCE, transport_type = FPE.Transport.BROADCAST, header_type = HEADER_1, transport_id = None): if destination != None: if transport_type == None: transport_type = FPE.Transport.BROADCAST @@ -53,7 +53,11 @@ class Packet: raise IOError("Packet with header type 2 must have a transport ID") self.header += self.destination.hash - self.ciphertext = self.destination.encrypt(self.data) + if self.packet_type != Packet.ANNOUNCE: + self.ciphertext = self.destination.encrypt(self.data) + else: + self.ciphertext = self.data + self.raw = self.header + self.ciphertext if len(self.raw) > self.MTU: diff --git a/FPE/__init__.py b/FPE/__init__.py index e9b5218..3a92e21 100755 --- a/FPE/__init__.py +++ b/FPE/__init__.py @@ -58,3 +58,13 @@ def log(msg, level=3): file = open(logfile, "a") file.write(logstring+"\n") file.close() + +def hexprint(data): + print(hexrep(hexrep)) + +def hexrep(data, delimit=True): + delimiter = ":" + if not delimit: + delimiter = "" + hexrep = delimiter.join("{:02x}".format(ord(c)) for c in data) + return hexrep \ No newline at end of file diff --git a/t.py b/t.py index bd3d6c7..d7209b4 100755 --- a/t.py +++ b/t.py @@ -19,30 +19,16 @@ 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 - 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)) +d1.announce() p1=Packet(d1, msg) -p1.send() +#p1.send() # p2=Packet(d2,"Test af msg") # p2.send()