From d65fab5a133cb7769beb24b7df81d356c12a6693 Mon Sep 17 00:00:00 2001 From: Michael Rasmussen Date: Sat, 11 Aug 2018 00:26:21 +0200 Subject: [PATCH] First working beta --- cryptonize.py | 23 +++++++------ db.py | 46 +++++++++++++++++++++---- user.py | 94 +++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 128 insertions(+), 35 deletions(-) diff --git a/cryptonize.py b/cryptonize.py index 9cc4631..d375f23 100644 --- a/cryptonize.py +++ b/cryptonize.py @@ -19,7 +19,7 @@ from nacl.secret import SecretBox from nacl.public import PrivateKey, Box -from nacl.utils import random +from nacl.utils import random, EncryptedMessage from nacl.encoding import HexEncoder import nacl.hash @@ -32,7 +32,7 @@ class Cryptonize: skey = self.sanitize_key(key) box = SecretBox(skey) cipher = box.encrypt(plain) - box = None + box = skey = None return cipher @@ -40,7 +40,7 @@ class Cryptonize: skey = self.sanitize_key(key) box = SecretBox(skey) plain = box.decrypt(cipher) - box = None + box = skey = None return plain @@ -66,14 +66,10 @@ class Cryptonize: key = key.encode('utf-8') size = len(key) if size < SecretBox.KEY_SIZE: - """We must pad""" - pad = None - for i in range(SecretBox.KEY_SIZE - size): - if pad is None: - pad = b'\0' - else: - pad += b'\0' - newkey = key + pad + """ We must pad """ + newkey = key + bytes(SecretBox.KEY_SIZE - size) + elif size > SecretBox.KEY_SIZE: + newkey = key[:SecretBox.KEY_SIZE] else: newkey = key @@ -94,4 +90,9 @@ class Cryptonize: return digest.decode() + def create_EncryptedMessage(self, payload): + nonce = payload[:SecretBox.NONCE_SIZE] + ciphertext = payload[SecretBox.NONCE_SIZE:] + return EncryptedMessage._from_parts( + nonce, ciphertext, nonce + ciphertext) diff --git a/db.py b/db.py index e904fb1..1d545b1 100644 --- a/db.py +++ b/db.py @@ -17,7 +17,23 @@ # You should have received a copy of the GNU General Public License # along with SecureMail. If not, see . +# mysql +# create table account ( +# id int auto_increment, +# token char(128) unique not null, +# cipher blob not null, +# primary key (id)); +# +# postgresql +# create table account ( +# id serial, +# token char(128) unique not null, +# cipher bytea not null, +# primary key (id)); + +import base64 from config import DBTYPE, DBHOST, DBPORT, DBUID, DBPWD, DBNAME +from cryptonize import Cryptonize class Singleton: def __init__(self, klass): @@ -36,11 +52,14 @@ class DB: def get_connection(self): if self.conn is None: if DBTYPE == 'mysql': - import mysql.connector - self.conn = mysql.connector.connect(host=DBHOST, port=DBPORT, user=DBUID, password=DBPWD, database=DBNAME) + #import MySQLdb + import MySQLdb + self.conn = MySQLdb.connect(host=DBHOST, port=DBPORT, user=DBUID, password=DBPWD, database=DBNAME) elif DBTYPE == 'postgresql': import psycopg2 self.conn = psycopg2.connect(host=DBHOST, port=DBPORT, user=DBUID, password=DBPWD, dbname=DBNAME) + else: + raise ValueError('{0}: Unsupported database'.format(DBTYPE)) return self.conn def __del__(self): @@ -52,20 +71,33 @@ class DBInterface: def load_user(key): conn = DB().get_connection() cursor = conn.cursor() - cursor.execute("select a.cipher from account a where id = '{0}'".format(key)) + cursor.execute("select a.cipher from account a where token = '{0}'".format(key)) row = cursor.fetchone() if row is None: obj = None else: - obj = row[0].tobytes() + c = Cryptonize() + msg = base64.b64decode(row[0]) + obj = c.create_EncryptedMessage(msg) cursor.close() return obj @staticmethod def store_user(key, cipher): + if DBTYPE == 'mysql': + from MySQLdb import Error as DBError + elif DBTYPE == 'postgresql': + from psycopg2 import Error as DBError conn = DB().get_connection() cursor = conn.cursor() - cursor.execute("insert into account(id, cipher) values(%s, %s)", (key, cipher)) - conn.commit() - cursor.close() + raw = base64.b64encode(cipher) + try: + cursor.execute("insert into account(token, cipher) values(%s, %s)", (key, raw)) + conn.commit() + except DBError as e: + print (e) + conn.rollback() + raise e + finally: + cursor.close() diff --git a/user.py b/user.py index 3b4c051..64673c7 100644 --- a/user.py +++ b/user.py @@ -17,14 +17,12 @@ # You should have received a copy of the GNU General Public License # along with SecureMail. If not, see . -try: - import cPickle as pickle -except: - import pickle +import pickle from db import DBInterface as DBI from cryptonize import Cryptonize +from nacl.public import PublicKey -class NoSuchUserException(Exception): +class NoSuchUser(Exception): pass class User: @@ -34,24 +32,48 @@ class User: def __init__(self, key=None): if key is not None: self.load(key) + else: + self.pubkeys = {} def store(self, key): crypto = Cryptonize() - cipher = crypto.symmetric_encrypt(key, pickle.dumps(self.__dict__)) + cipher = crypto.symmetric_encrypt(key, pickle.dumps(self)) DBI.store_user(crypto.generate_hash(key), cipher) def load(self, key): crypto = Cryptonize() cipher = DBI.load_user(crypto.generate_hash(key)) if cipher is None: - raise NoSuchUserException('{0}: User not found'.format(key)) + raise NoSuchUser('{0}: User not found'.format(key)) plain = crypto.symmetric_decrypt(key, cipher) try: obj = pickle.loads(plain) - self.__dict__.update(obj) + self.__dict__.update(obj.__dict__) except pickle.UnpicklingError as e: - raise NoSuchUserException(e) - + raise e + + def add_pubkey(self, email, key): + if email not in self.pubkeys: + self.pubkeys[email] = key.encode() + else: + raise KeyError('{0}: Exists'.format(email)) + + def update_pubkey(self, email, key): + self.pubkeys[email] = key.encode() + + def delete_pubkey(self, email): + if email in self.pubkeys: + del self.pubkeys[email] + + def get_pubkey(self, email): + if email in self.pubkeys: + key = self.pubkeys[email] + key = PublicKey(key) + else: + key = None + + return key + @property def name(self): return self._name @@ -62,25 +84,63 @@ class User: @property def email(self): - return self.email + return self._email @email.setter def email(self, email): self._email = email + @property + def pubkeys(self): + return self._pubkeys + + @pubkeys.setter + def pubkeys(self, pubkeys): + if type(pubkeys) is not type({}): + raise ValueError('Not dictionary') + self._pubkeys = pubkeys if __name__ == '__main__': try: u = User('test') for attr, value in u.__dict__.items(): print ('{0}: {1}'.format(attr, value)) + print ('{0} - {1} - {2}'.format(u.name, u.email, u.pubkeys)) + key = '' + for i in range(40): + key += '{0}'.format(i) + u = User() + u.name = 'testname1' + u.email = 'testname1@securemail.icu' + u.pubkeys = {'test': 'some test', 'test1': 'some test 1'} + try: + u.store(key) + except: + u = User(key) + for attr, value in u.__dict__.items(): + print ('{0}: {1}'.format(attr, value)) + print ('{0} - {1} - {2}'.format(u.name, u.email, u.pubkeys)) + from nacl.public import Box c = Cryptonize() - key = 'æselØre' #c.get_random_key() - cipher = c.symmetric_encrypt(key, pickle.dumps(u)) - obj = pickle.loads(c.symmetric_decrypt(key, cipher)) - for attr, value in obj.__dict__.items(): - print ('{0}: {1}'.format(attr, value)) - except NoSuchUserException: + keypair1 = c.get_key_pair() + keypair2 = c.get_key_pair() + try: + u.add_pubkey('test', keypair2[1]) + except KeyError: + u.update_pubkey('test', keypair2[1]) + bob_box = Box(keypair1[0], u.get_pubkey('test')) + message = "Kill all humans æøåÅØÆ" + encrypted = bob_box.encrypt(message.encode()) + alice_box = Box(keypair2[0], keypair1[1]) + plaintext = alice_box.decrypt(encrypted) + print (plaintext.decode()) +# c = Cryptonize() +# key = 'æselØre' #c.get_random_key() +# cipher = c.symmetric_encrypt(key, pickle.dumps(u)) +# obj = pickle.loads(c.symmetric_decrypt(key, cipher)) +# for attr, value in obj.__dict__.items(): +# print ('{0}: {1}'.format(attr, value)) + except NoSuchUser: u = User() u.name = 'testname' u.email = 'testname@securemail.icu' -- 2.39.2