From 4c92493bc25ce7ca3a459ea8ad26dea160dcc3d5 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 16 Mar 2018 11:40:37 +0100 Subject: [PATCH] Restructed to Identity --- FPE/Destination.py | 127 ++++++++++----------------------------- FPE/FlexPE.py | 11 +++- FPE/Identity.py | 147 ++++++++++++++++++++++++++++++++++++++++++++- t.py | 16 +++-- 4 files changed, 195 insertions(+), 106 deletions(-) diff --git a/FPE/Destination.py b/FPE/Destination.py index f3092be..7896b93 100755 --- a/FPE/Destination.py +++ b/FPE/Destination.py @@ -2,6 +2,7 @@ import base64 import math from Identity import Identity from Transport import Transport +from Packet import Packet from cryptography.fernet import Fernet from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend @@ -47,7 +48,7 @@ class Destination: return digest.finalize()[:10] - def __init__(self, direction, type, app_name, *aspects): + def __init__(self, identity, direction, type, app_name, *aspects): # Check input values and build name string if "." in app_name: raise ValueError("Dots can't be used in app names") if not type in Destination.types: raise ValueError("Unknown destination type") @@ -56,18 +57,19 @@ class Destination: self.direction = direction self.mtu = 0 + if identity == None: + identity = Identity() + identity.createKeys() + + self.identity = identity + aspects = aspects+(identity.hexhash,) + self.name = Destination.getDestinationName(app_name, *aspects) self.hash = Destination.getDestinationHash(app_name, *aspects) self.hexhash = self.hash.encode("hex_codec") self.callback = None - # Initialize keys to none - self.prv = None - self.pub = None - self.prv_bytes = None - self.pub_bytes = None - Transport.registerDestination(self) @@ -85,54 +87,33 @@ class Destination: self.callback(plaintext, self) - def createKey(self): + def createKeys(self): if self.type == Destination.PLAIN: raise TypeError("A plain destination does not hold any keys") if self.type == Destination.SINGLE: - self.prv = rsa.generate_private_key( - public_exponent=65337, - key_size=Destination.KEYSIZE, - backend=default_backend() - ) - self.prv_bytes = self.prv.private_bytes( - encoding=serialization.Encoding.DER, - format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=serialization.NoEncryption() - ) - self.pub = self.prv.public_key() - self.pub_bytes = self.pub.public_bytes( - encoding=serialization.Encoding.DER, - format=serialization.PublicFormat.SubjectPublicKeyInfo - ) - print("Keys created, private length is "+str(len(self.prv_bytes))) - print("Keys created, public length is "+str(len(self.pub_bytes))) - #+", public length is "+str(len(self.pub_bytes)))) + raise TypeError("A single destination holds keys through an Identity instance") if self.type == Destination.GROUP: self.prv_bytes = Fernet.generate_key() self.prv = Fernet(self.prv_bytes) - def getKey(self): + def getPrivateKey(self): if self.type == Destination.PLAIN: raise TypeError("A plain destination does not hold any keys") + elif self.type == Destination.SINGLE: + raise TypeError("A single destination holds keys through an Identity instance") else: return self.prv_bytes - def loadKey(self, key): + def loadPrivateKey(self, key): if self.type == Destination.PLAIN: raise TypeError("A plain destination does not hold any keys") if self.type == Destination.SINGLE: - self.prv_bytes = key - self.prv = serialization.load_der_private_key(self.prv_bytes, password=None,backend=default_backend()) - self.pub = self.prv.public_key() - self.pub_bytes = self.pub.public_bytes( - encoding=serialization.Encoding.DER, - format=serialization.PublicFormat.SubjectPublicKeyInfo - ) + raise TypeError("A single destination holds keys through an Identity instance") if self.type == Destination.GROUP: self.prv_bytes = key @@ -141,39 +122,16 @@ class Destination: def loadPublicKey(self, key): if self.type != Destination.SINGLE: raise TypeError("Only the \"single\" destination type can hold a public key") - - self.pub_bytes = key - self.pub = load_der_public_key(self.pub_bytes, backend=default_backend()) + else: + raise TypeError("A single destination holds keys through an Identity instance") def encrypt(self, plaintext): if self.type == Destination.PLAIN: return plaintext - if self.type == Destination.SINGLE and self.prv != None: - chunksize = (Destination.KEYSIZE-Destination.PADDINGSIZE)/8 - chunks = int(math.ceil(len(plaintext)/(float(chunksize)))) - print("Plaintext size is "+str(len(plaintext))+", with "+str(chunks)+" chunks") - - ciphertext = ""; - for chunk in range(chunks): - start = chunk*chunksize - end = (chunk+1)*chunksize - 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]))) - - ciphertext += self.pub.encrypt( - plaintext[start:end], - padding.OAEP( - mgf=padding.MGF1(algorithm=hashes.SHA1()), - algorithm=hashes.SHA1(), - label=None - ) - ) - print("Plaintext encrypted, ciphertext length is "+str(len(ciphertext))+" bytes.") - return ciphertext + if self.type == Destination.SINGLE and self.identity != None: + return self.identity.encrypt(plaintext) if self.type == Destination.GROUP and self.prv != None: try: @@ -186,44 +144,21 @@ class Destination: if self.type == Destination.PLAIN: return ciphertext - if self.type == Destination.SINGLE and self.prv != None: - print("Ciphertext length is "+str(len(ciphertext))+". ") - chunksize = (Destination.KEYSIZE)/8 - chunks = int(math.ceil(len(ciphertext)/(float(chunksize)))) - - plaintext = ""; - for chunk in range(chunks): - start = chunk*chunksize - end = (chunk+1)*chunksize - 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]))) - - plaintext += self.prv.decrypt( - ciphertext[start:end], - padding.OAEP( - mgf=padding.MGF1(algorithm=hashes.SHA1()), - algorithm=hashes.SHA1(), - label=None - ) - ) - return plaintext; + if self.type == Destination.SINGLE and self.identity != None: + return self.identity.decrypt(ciphertext) if self.type == Destination.GROUP: return self.prv.decrypt(base64.urlsafe_b64encode(ciphertext)) def sign(self, message): - if self.type == Destination.SINGLE and self.prv != None: - signer = self.prv.signer( - padding.PSS( - mgf=padding.MGF1(hashes.SHA256()), - salt_length=padding.PSS.MAX_LENGTH - ), - hashes.SHA256() - ) - signer.update(message) - return signer.finalize() + if self.type == Destination.SINGLE and self.identity != None: + return self.identity.sign(message) else: - return None \ No newline at end of file + return None + + + # Creates an announce packet for this destination. + # Application specific data can be added to the announce. + def announce(self,app_data=None): + pass diff --git a/FPE/FlexPE.py b/FPE/FlexPE.py index 89bc44d..3f06eb8 100755 --- a/FPE/FlexPE.py +++ b/FPE/FlexPE.py @@ -17,9 +17,14 @@ class FlexPE: configpath = configdir+"/config" - def __init__(self): - if os.path.isfile(FlexPE.configpath): - self.config = ConfigObj(FlexPE.configpath) + def __init__(self,config=None): + if config != None: + self.configpath = config + else: + self.configpath = FlexPE.configpath + + if os.path.isfile(self.configpath): + self.config = ConfigObj(self.configpath) else: print("Could not load config file, creating default configuration...") self.createDefaultConfig() diff --git a/FPE/Identity.py b/FPE/Identity.py index 748864f..48a77a4 100644 --- a/FPE/Identity.py +++ b/FPE/Identity.py @@ -1,7 +1,152 @@ +import base64 +import math +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric import padding class Identity: # Configure key size KEYSIZE = 1536; # Padding size, not configurable - PADDINGSIZE= 336; \ No newline at end of file + PADDINGSIZE= 336; + + def __init__(self): + # Initialize keys to none + self.prv = None + self.pub = None + self.prv_bytes = None + self.pub_bytes = None + self.hash = None + self.hexhash = None + + self.createKeys() + + @staticmethod + def getHash(pub_key): + digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + digest.update(pub_key) + + return digest.finalize()[:10] + + def createKeys(self): + self.prv = rsa.generate_private_key( + public_exponent=65337, + key_size=Identity.KEYSIZE, + backend=default_backend() + ) + self.prv_bytes = self.prv.private_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption() + ) + self.pub = self.prv.public_key() + self.pub_bytes = self.pub.public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.SubjectPublicKeyInfo + ) + + 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))) + + def getPrivateKey(self): + return self.prv_bytes + + def getPublicKey(self): + return self.pub_bytes + + def loadPrivateKey(self, key): + self.prv_bytes = key + self.prv = serialization.load_der_private_key(self.prv_bytes, password=None,backend=default_backend()) + self.pub = self.prv.public_key() + self.pub_bytes = self.pub.public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.SubjectPublicKeyInfo + ) + + def loadPublicKey(self, key): + self.pub_bytes = key + self.pub = load_der_public_key(self.pub_bytes, backend=default_backend()) + + def saveIdentity(self): + pass + + def loadIdentity(self): + pass + + def encrypt(self, plaintext): + 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") + + ciphertext = ""; + for chunk in range(chunks): + start = chunk*chunksize + end = (chunk+1)*chunksize + 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]))) + + ciphertext += self.pub.encrypt( + plaintext[start:end], + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ) + ) + 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") + + + def decrypt(self, ciphertext): + if self.prv != None: + print("Ciphertext length is "+str(len(ciphertext))+". ") + chunksize = (Identity.KEYSIZE)/8 + chunks = int(math.ceil(len(ciphertext)/(float(chunksize)))) + + plaintext = ""; + for chunk in range(chunks): + start = chunk*chunksize + end = (chunk+1)*chunksize + 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]))) + + plaintext += self.prv.decrypt( + ciphertext[start:end], + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ) + ) + return plaintext; + else: + raise KeyError("Decryption failed because identity does not hold a private key") + + + def sign(self, message): + if self.prv != None: + signer = self.prv.signer( + padding.PSS( + mgf=padding.MGF1(hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH + ), + hashes.SHA256() + ) + signer.update(message) + return signer.finalize() + else: + raise KeyError("Signing failed because identity does not hold a private key") + diff --git a/t.py b/t.py index 2d81d29..18baed2 100755 --- a/t.py +++ b/t.py @@ -3,6 +3,7 @@ # from FPE import FlexPE from FPE import * # from FPE import Destination +import os import time def testCallback(message, receiver): @@ -11,17 +12,20 @@ def testCallback(message, receiver): print("----------") +#fpe = FlexPE(config=os.path.expanduser("~")+"/.flexpe/config.test") fpe = FlexPE() -d1=Destination(Destination.IN, Destination.SINGLE, "messenger", "markqvist") -d1.createKey() +identity = Identity() + +d1=Destination(identity, Destination.IN, Destination.SINGLE, "messenger", "user") d1.setCallback(testCallback) -d2=Destination(Destination.IN, Destination.PLAIN, "plainchat", "markqvist") -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.pub +print d1.identity.pub print "---" print @@ -32,7 +36,7 @@ for x in range(300): msg += "a" signed = d1.sign(msg) sl = len(signed) -pl = len(d1.pub_bytes) +pl = len(d1.identity.pub_bytes) print("Signature length is "+str(sl)) print("Minimum announce is "+str(pl+sl+8))