diff options
| author | drebs <drebs@leap.se> | 2012-12-18 18:51:01 -0200 | 
|---|---|---|
| committer | drebs <drebs@leap.se> | 2012-12-18 18:51:01 -0200 | 
| commit | 6306a61d8c6c840a09ea9aa26e439a40dc9b5a9a (patch) | |
| tree | 5d63e737a7f3169f1fece8afef5d10a4a458dd0a /__init__.py | |
| parent | 2d4fc3ee887fd52ac06fdd6f99bdc4b445a79463 (diff) | |
Refactor and symmetric encryption
Diffstat (limited to '__init__.py')
| -rw-r--r-- | __init__.py | 245 | 
1 files changed, 78 insertions, 167 deletions
diff --git a/__init__.py b/__init__.py index 45034561..835111a5 100644 --- a/__init__.py +++ b/__init__.py @@ -3,170 +3,81 @@  """A U1DB implementation for using Object Stores as its persistence layer."""  import os -import gnupg - -class GPGWrapper(): -    """ -    This is a temporary class for handling GPG requests, and should be -    replaced by a more general class used throughout the project. -    """ - -    GNUPG_HOME    = os.environ['HOME'] + "/.config/leap/gnupg" -    GNUPG_BINARY  = "/usr/bin/gpg" # this has to be changed based on OS - -    def __init__(self, gpghome=GNUPG_HOME, gpgbinary=GNUPG_BINARY): -        self.gpg = gnupg.GPG(gnupghome=gpghome, gpgbinary=gpgbinary) - -    def find_key(self, email): -        """ -        Find user's key based on their email. -        """ -        for key in self.gpg.list_keys(): -            for uid in key['uids']: -                if re.search(email, uid): -                    return key -        raise LookupError("GnuPG public key for %s not found!" % email) - -    def encrypt(self, data, recipient, sign=None, always_trust=False, -                passphrase=None, symmetric=False): -        return self.gpg.encrypt(data, recipient, sign=sign, -                                always_trust=always_trust, -                                passphrase=passphrase, symmetric=symmetric) - -    def decrypt(self, data, always_trust=False, passphrase=None): -        return self.gpg.decrypt(data, always_trust=always_trust, -                                passphrase=passphrase) - -    def import_keys(self, data): -        return self.gpg.import_keys(data) - - -#---------------------------------------------------------------------------- -# u1db Transaction and Sync logs. -#---------------------------------------------------------------------------- - -class SimpleLog(object): -    def __init__(self): -        self._log = [] - -    def _set_log(self, log): -        self._log = log - -    def _get_log(self): -        return self._log - -    log = property( -        _get_log, _set_log, doc="Log contents.") - -    def append(self, msg): -        self._log.append(msg) - -    def reduce(self, func, initializer=None): -        return reduce(func, self.log, initializer) - -    def map(self, func): -        return map(func, self.log) - -    def filter(self, func): -        return filter(func, self.log) - - -class TransactionLog(SimpleLog): -    """ -    An ordered list of (generation, doc_id, transaction_id) tuples. -    """ - -    def _set_log(self, log): -        self._log = log - -    def _get_log(self): -        return sorted(self._log, reverse=True) - -    log = property( -        _get_log, _set_log, doc="Log contents.") - -    def get_generation(self): -        """ -        Return the current generation. -        """ -        gens = self.map(lambda x: x[0]) -        if not gens: -            return 0 -        return max(gens) - -    def get_generation_info(self): -        """ -        Return the current generation and transaction id. -        """ -        if not self._log: -            return(0, '') -        info = self.map(lambda x: (x[0], x[2])) -        return reduce(lambda x, y: x if (x[0] > y[0]) else y, info) - -    def get_trans_id_for_gen(self, gen): -        """ -        Get the transaction id corresponding to a particular generation. -        """ -        log = self.reduce(lambda x, y: y if y[0] == gen else x) -        if log is None: -            return None -        return log[2] - -    def whats_changed(self, old_generation): -        """ -        Return a list of documents that have changed since old_generation. -        """ -        results = self.filter(lambda x: x[0] > old_generation) -        seen = set() -        changes = [] -        newest_trans_id = '' -        for generation, doc_id, trans_id in results: -            if doc_id not in seen: -                changes.append((doc_id, generation, trans_id)) -                seen.add(doc_id) -        if changes: -            cur_gen = changes[0][1]  # max generation -            newest_trans_id = changes[0][2] -            changes.reverse() -        else: -            results = self.log -            if not results: -                cur_gen = 0 -                newest_trans_id = '' -            else: -                cur_gen, _, newest_trans_id = results[0] - -        return cur_gen, newest_trans_id, changes -         - - -class SyncLog(SimpleLog): -    """ -    A list of (replica_id, generation, transaction_id) tuples. -    """ - -    def find_by_replica_uid(self, replica_uid): -        if not self.log: -            return () -        return self.reduce(lambda x, y: y if y[0] == replica_uid else x) - -    def get_replica_gen_and_trans_id(self, other_replica_uid): -        """ -        Return the last known generation and transaction id for the other db -        replica. -        """ -        info = self.find_by_replica_uid(other_replica_uid) -        if not info: -            return (0, '') -        return (info[1], info[2]) - -    def set_replica_gen_and_trans_id(self, other_replica_uid, -                                      other_generation, other_transaction_id): -        """ -        Set the last-known generation and transaction id for the other -        database replica. -        """ -        self.log = self.filter(lambda x: x[0] != other_replica_uid) -        self.append((other_replica_uid, other_generation, -                     other_transaction_id)) - +import string +import random +import cStringIO +from soledad.util import GPGWrapper + +class Soledad(object): + +    PREFIX        = os.environ['HOME']  + '/.config/leap/soledad' +    SECRET_PATH   = PREFIX + '/secret.gpg' +    GNUPG_HOME    = PREFIX + '/gnupg' +    SECRET_LENGTH = 50 + +    def __init__(self, user_email, gpghome=None): +        self._user_email = user_email +        if not os.path.isdir(self.PREFIX): +            os.makedirs(self.PREFIX) +        if not gpghome: +            gpghome = self.GNUPG_HOME +        self._gpg = GPGWrapper(gpghome=gpghome) +        # load OpenPGP keypair +        if not self._has_openpgp_keypair(): +            self._gen_openpgp_keypair() +        self._load_openpgp_keypair() +        # load secret +        if not self._has_secret(): +            self._gen_secret() +        self._load_secret() + +    def _has_secret(self): +        if os.path.isfile(self.SECRET_PATH): +            return True +        return False + +    def _load_secret(self): +        try: +            with open(self.SECRET_PATH) as f: +               self._secret = self._gpg.decrypt(f.read()) +        except IOError as e: +           raise IOError('Failed to open secret file %s.' % self.SECRET_PATH) + +    def _gen_secret(self): +        self._secret = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(self.SECRET_LENGTH)) +        cyphertext = self._gpg.encrypt(self._secret, self._fingerprint, self._fingerprint) +        f = open(self.SECRET_PATH, 'w') +        f.write(str(cyphertext)) +        f.close() + + +    def _has_openpgp_keypair(self): +        if self._gpg.find_key(self._user_email): +            return True +        return False + +    def _gen_openpgp_keypair(self): +        params = self._gpg.gen_key_input( +          key_type='RSA', +          key_length=4096, +          name_real=self._user_email, +          name_email=self._user_email, +          name_comment='Generated by LEAP Soledad.') +        self._gpg.gen_key(params) + +    def _load_openpgp_keypair(self): +        self._fingerprint = self._gpg.find_key(self._user_email)['fingerprint'] + +    def encrypt(self, data, sign=None, passphrase=None, symmetric=False): +        return str(self._gpg.encrypt(data, self._fingerprint, sign=sign, +                                     passphrase=passphrase, symmetric=symmetric)) + +    def encrypt_symmetric(self, data, sign=None): +        return self.encrypt(data, sign=sign, passphrase=self._secret, +                            symmetric=True) + +    def decrypt(self, data, passphrase=None, symmetric=False): +        return str(self._gpg.decrypt(data, passphrase=passphrase)) + +    def decrypt_symmetric(self, data): +        return self.decrypt(data, passphrase=self._secret)  | 
