From 7ef0042650fe0a5e12373fd6ef438f138afe65b2 Mon Sep 17 00:00:00 2001 From: Michael Rasmussen Date: Mon, 13 Aug 2018 22:51:15 +0200 Subject: [PATCH] Reorder Signed-off-by: Michael Rasmussen --- __init__.py | 0 config.py | 37 --------- cryptonize.py | 111 --------------------------- db.py | 202 ------------------------------------------------- securemail.e4p | 12 +-- user.py | 142 ---------------------------------- 6 files changed, 6 insertions(+), 498 deletions(-) delete mode 100644 __init__.py delete mode 100644 config.py delete mode 100644 cryptonize.py delete mode 100644 db.py delete mode 100644 user.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/config.py b/config.py deleted file mode 100644 index bd71642..0000000 --- a/config.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2018 Michael Rasmussen - -# This file is part of SecureMail. - -# SecureMail is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# SecureMail is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with SecureMail. If not, see . - -###### REQUIREMENTS ####### -# python3-nacl # -# python3-mysqldb # -# python3-psycopg2 # -# python3-apsw # -# mysql, postgresql or sqlite # -############################### - -#DBTYPE = "mysql" -#DBHOST = "localhost" # default value -#DBPORT = 3306 # default value -#DBTYPE = "postgresql" -#DBHOST = "localhost" # default value -#DBPORT = 5432 # default value -DBTYPE = "sqlite" -#DBUID = "backend" # default value -#DBPWD = "clV77B2ZJQxr" # default value -DBNAME = "securemail" # if DBTYPE is sqlite: ./DBNAME + .db diff --git a/cryptonize.py b/cryptonize.py deleted file mode 100644 index 7a598d3..0000000 --- a/cryptonize.py +++ /dev/null @@ -1,111 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2018 Michael Rasmussen - -# This file is part of SecureMail. - -# SecureMail is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# SecureMail is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with SecureMail. If not, see . - -from nacl import __version__ as NACL_VERSION -from nacl.secret import SecretBox -from nacl.public import PrivateKey, Box -from nacl.utils import random, EncryptedMessage -from nacl.encoding import HexEncoder -import nacl.hash - -class Cryptonize: - """ - Encrypt and decrypt objects - """ - - def symmetric_encrypt(self, key, plain): - skey = self.sanitize_key(key) - box = SecretBox(skey) - if NACL_VERSION < "1.1.0": - nonce = random(SecretBox.NONCE_SIZE) - cipher = box.encrypt(plain, nonce) - else: - cipher = box.encrypt(plain) - box = skey = None - - return cipher - - def symmetric_decrypt(self, key, cipher): - skey = self.sanitize_key(key) - box = SecretBox(skey) - plain = box.decrypt(cipher) - box = skey = None - - return plain - - def asymmetric_encrypt(self, privkey, pubkey, plain): - if not isinstance(plain, bytes): - plain = plain.encode('utf-8') - box = Box(privkey, pubkey) - if NACL_VERSION < "1.1.0": - nonce = random(Box.NONCE_SIZE) - cipher = box.encrypt(plain, nonce) - else: - cipher = box.encrypt(plain) - box = None - - return cipher - - def asymmetric_decrypt(self, privkey, pubkey, cipher): - if not isinstance(cipher, bytes): - cipher = cipher.encode('utf-8') - box = Box(privkey, pubkey) - plain = box.decrypt(cipher) - box = None - - return plain - - def get_random_key(self): - return random(SecretBox.KEY_SIZE) - - def sanitize_key(self, key): - if not isinstance(key, bytes): - key = key.encode('utf-8') - size = len(key) - if size < SecretBox.KEY_SIZE: - """ We must pad """ - newkey = key + bytes(SecretBox.KEY_SIZE - size) - elif size > SecretBox.KEY_SIZE: - newkey = key[:SecretBox.KEY_SIZE] - else: - newkey = key - - - return newkey - - def get_key_pair(self): - privkey = PrivateKey.generate() - pubkey = privkey.public_key - - return (privkey, pubkey) - - def generate_hash(self, key): - if not isinstance(key, bytes): - key = key.encode('utf-8') - HASHER = nacl.hash.sha512 - digest = HASHER(key, encoder=HexEncoder) - - 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 deleted file mode 100644 index 93eea09..0000000 --- a/db.py +++ /dev/null @@ -1,202 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2018 Michael Rasmussen - -# This file is part of SecureMail. - -# SecureMail is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# SecureMail is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with SecureMail. If not, see . - -# sqlite -sqlite_sql = """create table account ( -id int auto_increment, -token char(128) unique not null, -cipher text not null, -primary key (id))""" - -# mysql -mysql_sql = """create table account ( -id int auto_increment, -token char(128) unique not null, -cipher text not null, -primary key (id))""" - -# postgresql -postgresql_sql = """create table account ( -id serial, -token char(128) unique not null, -cipher bytea not null, -primary key (id))""" - -import base64 -from config import DBTYPE, DBNAME -try: - from config import DBUID -except ImportError: - DBUID = 'backend' -try: - from config import DBPWD -except ImportError: - DBPWD = 'clV77B2ZJQxr' -try: - from config import DBHOST -except ImportError: - DBHOST = 'localhost' -try: - from config import DBPORT -except ImportError: - if DBTYPE == 'mysql': - DBPORT = 3306 - elif DBTYPE == 'postgresql': - DBPORT = 5432 -from cryptonize import Cryptonize - -class Singleton: - def __init__(self, klass): - self.klass = klass - self.instance = None - - def __call__(self, *args, **kwargs): - if self.instance == None: - self.instance = self.klass(*args, **kwargs) - return self.instance - -@Singleton -class DB: - conn = None - - def get_connection(self): - if self.conn is None: - if DBTYPE == 'mysql': - import MySQLdb - self.conn = MySQLdb.connect(host=DBHOST, port=DBPORT, user=DBUID, passwd=DBPWD, db=DBNAME) - elif DBTYPE == 'postgresql': - import psycopg2 - self.conn = psycopg2.connect(host=DBHOST, port=DBPORT, user=DBUID, password=DBPWD, dbname=DBNAME) - elif DBTYPE == 'sqlite': - import apsw - self.conn = apsw.Connection('./{0}.db'.format(DBNAME)) - else: - raise ValueError('{0}: Unsupported database'.format(DBTYPE)) - return self.conn - - def __del__(self): - if self.conn is not None: - self.conn.close() - -class DBInterface: - @staticmethod - def load_user(key): - conn = DB().get_connection() - cursor = conn.cursor() - cursor.execute("select a.cipher from account a where token = '{0}'".format(key)) - row = cursor.fetchone() - if row is None: - obj = None - else: - 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 - elif DBTYPE == 'sqlite': - from apsw import Error as DBError - conn = DB().get_connection() - cursor = conn.cursor() - raw = base64.b64encode(cipher) - try: - if DBTYPE != 'sqlite': - cursor.execute("insert into account(token, cipher) values(%s, %s)", (key, raw)) - conn.commit() - else: - cursor.execute('begin') - cursor.execute("insert into account(token, cipher) values(?, ?)", (key, raw)) - cursor.execute('commit') - except DBError as e: - print (e) - if DBTYPE != 'sqlite': - conn.rollback() - else: - cursor.execute('rollback') - raise e - finally: - cursor.close() - - @staticmethod - def create_database(): - if DBTYPE == 'mysql': - from MySQLdb import Error as DBError - elif DBTYPE == 'postgresql': - from psycopg2 import Error as DBError - elif DBTYPE == 'sqlite': - from apsw import Error as DBError - conn = DB().get_connection() - cursor = conn.cursor() - try: - if DBTYPE != 'sqlite': - if DBTYPE == 'mysql': - sql = mysql_sql - elif DBTYPE == 'postgresql': - sql = postgresql_sql - cursor.execute(sql) - conn.commit() - else: - cursor.execute('begin') - cursor.execute(sqlite_sql) - cursor.execute('commit') - except DBError as e: - if DBTYPE != 'sqlite': - conn.rollback() - else: - cursor.execute('rollback') - raise e - finally: - cursor.close() - -def main(): - from optparse import OptionParser - - usage = "usage: %prog [options] arg" - parser = OptionParser(usage) - parser.add_option("-c", "--create", action="store_true", dest="create", - help="Create tables in database using config.py", default=False) - parser.add_option("-v", "--verbose", action="store_true", dest="verbose", - help="Run in verbose mode", default=False) - (options, args) = parser.parse_args() - - if options.create: - try: - if options.verbose: - print("Creating empty database") - print("Database Engine: {0}".format(DBTYPE)) - if DBTYPE != 'sqlite': - print("Database Host: {0}".format(DBHOST)) - print("Database Port: {0}".format(DBPORT)) - else: - print("Database File: ./{0}.db".format(DBNAME)) - DBInterface.create_database() - print("Database created") - except Exception as e: - print("Creating database failed!") - print(e) - -if __name__ == '__main__': - main() diff --git a/securemail.e4p b/securemail.e4p index d4dcc00..fb5a1b7 100644 --- a/securemail.e4p +++ b/securemail.e4p @@ -14,17 +14,17 @@ mir@datanom.net - __init__.py - config.py - cryptonize.py - db.py - user.py + app/backend/__init__.py + app/backend/config.py + app/backend/cryptonize.py + app/backend/db.py + app/backend/user.py .gitignore securemail.e4p - user.py + app/backend/user.py Git diff --git a/user.py b/user.py deleted file mode 100644 index a4ab2f7..0000000 --- a/user.py +++ /dev/null @@ -1,142 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2018 Michael Rasmussen - -# This file is part of SecureMail. - -# SecureMail is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# SecureMail is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with SecureMail. If not, see . - -import pickle -from db import DBInterface as DBI -from cryptonize import Cryptonize -from nacl.public import PublicKey - -class NoSuchUser(Exception): - pass - -class User: - """ - Class implementing the backend users - """ - 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)) - 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 NoSuchUser('{0}: User not found'.format(key)) - plain = crypto.symmetric_decrypt(key, cipher) - try: - obj = pickle.loads(plain) - self.__dict__.update(obj.__dict__) - except pickle.UnpicklingError as 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 - - @name.setter - def name(self, name): - self._name = name - - @property - def email(self): - 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)) - c = Cryptonize() - 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]) - message = "Kill all humans æøåÅØÆ" - print ("Message to encrypt: {0}".format(message)) - encrypted = c.asymmetric_encrypt(keypair1[0], u.get_pubkey('test'), message) - print ("Message encrypted: {0}".format(encrypted)) - plaintext = c.asymmetric_decrypt(keypair2[0], keypair1[1], encrypted) - print("Message decrypted: {0}".format(plaintext.decode())) - except NoSuchUser: - u = User() - u.name = 'testname' - u.email = 'testname@securemail.icu' - u.store('test') - except Exception as e: - print (e) -- 2.39.5