--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2018 Michael Rasmussen <mir@datanom.net>
+
+# 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 <https://www.gnu.org/licenses/>.
+
+from nacl.secret import SecretBox
+from nacl.public import PrivateKey, Box
+from nacl.utils import random
+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)
+ cipher = box.encrypt(plain)
+ box = None
+
+ return cipher
+
+ def symmetric_decrypt(self, key, cipher):
+ skey = self.sanitize_key(key)
+ box = SecretBox(skey)
+ plain = box.decrypt(cipher)
+ box = None
+
+ return plain
+
+ def asymmetric_encrypt(self, privkey, pubkey, plain):
+ box = Box(privkey, pubkey)
+ cipher = box.encrypt(plain)
+ box = None
+
+ return cipher
+
+ def asymmetric_decrypt(self, privkey, pubkey, cipher):
+ 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"""
+ pad = None
+ for i in range(SecretBox.KEY_SIZE - size):
+ if pad is None:
+ pad = b'\0'
+ else:
+ pad += b'\0'
+ newkey = key + pad
+ 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()
+
+
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2018 Michael Rasmussen <mir@datanom.net>
+
+# 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 <https://www.gnu.org/licenses/>.
+
+from config import DBTYPE, DBHOST, DBPORT, DBUID, DBPWD, DBNAME
+
+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 mysql.connector
+ self.conn = mysql.connector.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)
+ 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 id = '{0}'".format(key))
+ row = cursor.fetchone()
+ if row is None:
+ obj = None
+ else:
+ obj = row[0].tobytes()
+ cursor.close()
+
+ return obj
+
+ @staticmethod
+ def store_user(key, cipher):
+ conn = DB().get_connection()
+ cursor = conn.cursor()
+ cursor.execute("insert into account(id, cipher) values(%s, %s)", (key, cipher))
+ conn.commit()
+ cursor.close()
--- /dev/null
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2018 Michael Rasmussen <mir@datanom.net>
+
+# 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 <https://www.gnu.org/licenses/>.
+
+try:
+ import cPickle as pickle
+except:
+ import pickle
+from db import DBInterface as DBI
+from cryptonize import Cryptonize
+
+class NoSuchUserException(Exception):
+ pass
+
+class User:
+ """
+ Class implementing the backend users
+ """
+ def __init__(self, key=None):
+ if key is not None:
+ self.load(key)
+
+ def store(self, key):
+ crypto = Cryptonize()
+ cipher = crypto.symmetric_encrypt(key, pickle.dumps(self.__dict__))
+ 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))
+ plain = crypto.symmetric_decrypt(key, cipher)
+ try:
+ obj = pickle.loads(plain)
+ self.__dict__.update(obj)
+ except pickle.UnpicklingError as e:
+ raise NoSuchUserException(e)
+
+ @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
+
+
+if __name__ == '__main__':
+ try:
+ u = User('test')
+ for attr, value in u.__dict__.items():
+ print ('{0}: {1}'.format(attr, value))
+ 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:
+ u = User()
+ u.name = 'testname'
+ u.email = 'testname@securemail.icu'
+ u.store('test')
+ except Exception as e:
+ print (e)