Restructed to Identity

This commit is contained in:
Mark Qvist 2018-03-16 11:40:37 +01:00
parent 5fcbb5d338
commit 4c92493bc2
4 changed files with 195 additions and 106 deletions

View File

@ -2,6 +2,7 @@ import base64
import math import math
from Identity import Identity from Identity import Identity
from Transport import Transport 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
@ -47,7 +48,7 @@ class Destination:
return digest.finalize()[:10] 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 # Check input values and build name string
if "." in app_name: raise ValueError("Dots can't be used in app names") 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") if not type in Destination.types: raise ValueError("Unknown destination type")
@ -56,18 +57,19 @@ class Destination:
self.direction = direction self.direction = direction
self.mtu = 0 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.name = Destination.getDestinationName(app_name, *aspects)
self.hash = Destination.getDestinationHash(app_name, *aspects) self.hash = Destination.getDestinationHash(app_name, *aspects)
self.hexhash = self.hash.encode("hex_codec") self.hexhash = self.hash.encode("hex_codec")
self.callback = None self.callback = None
# Initialize keys to none
self.prv = None
self.pub = None
self.prv_bytes = None
self.pub_bytes = None
Transport.registerDestination(self) Transport.registerDestination(self)
@ -85,54 +87,33 @@ class Destination:
self.callback(plaintext, self) self.callback(plaintext, self)
def createKey(self): def createKeys(self):
if self.type == Destination.PLAIN: if self.type == Destination.PLAIN:
raise TypeError("A plain destination does not hold any keys") raise TypeError("A plain destination does not hold any keys")
if self.type == Destination.SINGLE: if self.type == Destination.SINGLE:
self.prv = rsa.generate_private_key( raise TypeError("A single destination holds keys through an Identity instance")
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))))
if self.type == Destination.GROUP: if self.type == Destination.GROUP:
self.prv_bytes = Fernet.generate_key() self.prv_bytes = Fernet.generate_key()
self.prv = Fernet(self.prv_bytes) self.prv = Fernet(self.prv_bytes)
def getKey(self): def getPrivateKey(self):
if self.type == Destination.PLAIN: if self.type == Destination.PLAIN:
raise TypeError("A plain destination does not hold any keys") 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: else:
return self.prv_bytes return self.prv_bytes
def loadKey(self, key): def loadPrivateKey(self, key):
if self.type == Destination.PLAIN: if self.type == Destination.PLAIN:
raise TypeError("A plain destination does not hold any keys") raise TypeError("A plain destination does not hold any keys")
if self.type == Destination.SINGLE: if self.type == Destination.SINGLE:
self.prv_bytes = key raise TypeError("A single destination holds keys through an Identity instance")
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
)
if self.type == Destination.GROUP: if self.type == Destination.GROUP:
self.prv_bytes = key self.prv_bytes = key
@ -141,39 +122,16 @@ class Destination:
def loadPublicKey(self, key): def loadPublicKey(self, key):
if self.type != Destination.SINGLE: if self.type != Destination.SINGLE:
raise TypeError("Only the \"single\" destination type can hold a public key") raise TypeError("Only the \"single\" destination type can hold a public key")
else:
self.pub_bytes = key raise TypeError("A single destination holds keys through an Identity instance")
self.pub = load_der_public_key(self.pub_bytes, backend=default_backend())
def encrypt(self, plaintext): def encrypt(self, plaintext):
if self.type == Destination.PLAIN: if self.type == Destination.PLAIN:
return plaintext return plaintext
if self.type == Destination.SINGLE and self.prv != None: if self.type == Destination.SINGLE and self.identity != None:
chunksize = (Destination.KEYSIZE-Destination.PADDINGSIZE)/8 return self.identity.encrypt(plaintext)
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.GROUP and self.prv != None: if self.type == Destination.GROUP and self.prv != None:
try: try:
@ -186,44 +144,21 @@ class Destination:
if self.type == Destination.PLAIN: if self.type == Destination.PLAIN:
return ciphertext return ciphertext
if self.type == Destination.SINGLE and self.prv != None: if self.type == Destination.SINGLE and self.identity != None:
print("Ciphertext length is "+str(len(ciphertext))+". ") return self.identity.decrypt(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.GROUP: if self.type == Destination.GROUP:
return self.prv.decrypt(base64.urlsafe_b64encode(ciphertext)) return self.prv.decrypt(base64.urlsafe_b64encode(ciphertext))
def sign(self, message): def sign(self, message):
if self.type == Destination.SINGLE and self.prv != None: if self.type == Destination.SINGLE and self.identity != None:
signer = self.prv.signer( return self.identity.sign(message)
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
signer.update(message)
return signer.finalize()
else: else:
return None 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

View File

@ -17,9 +17,14 @@ class FlexPE:
configpath = configdir+"/config" configpath = configdir+"/config"
def __init__(self): def __init__(self,config=None):
if os.path.isfile(FlexPE.configpath): if config != None:
self.config = ConfigObj(FlexPE.configpath) self.configpath = config
else:
self.configpath = FlexPE.configpath
if os.path.isfile(self.configpath):
self.config = ConfigObj(self.configpath)
else: else:
print("Could not load config file, creating default configuration...") print("Could not load config file, creating default configuration...")
self.createDefaultConfig() self.createDefaultConfig()

View File

@ -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: class Identity:
# Configure key size # Configure key size
KEYSIZE = 1536; KEYSIZE = 1536;
# Padding size, not configurable # Padding size, not configurable
PADDINGSIZE= 336; 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")

16
t.py
View File

@ -3,6 +3,7 @@
# from FPE import FlexPE # from FPE import FlexPE
from FPE import * from FPE import *
# from FPE import Destination # from FPE import Destination
import os
import time import time
def testCallback(message, receiver): def testCallback(message, receiver):
@ -11,17 +12,20 @@ def testCallback(message, receiver):
print("----------") print("----------")
#fpe = FlexPE(config=os.path.expanduser("~")+"/.flexpe/config.test")
fpe = FlexPE() fpe = FlexPE()
d1=Destination(Destination.IN, Destination.SINGLE, "messenger", "markqvist") identity = Identity()
d1.createKey()
d1=Destination(identity, Destination.IN, Destination.SINGLE, "messenger", "user")
d1.setCallback(testCallback) d1.setCallback(testCallback)
d2=Destination(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 d1.name print d1.name
print d1.hexhash print d1.hexhash
print d1.pub print d1.identity.pub
print "---" print "---"
print print
@ -32,7 +36,7 @@ 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.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))