summaryrefslogtreecommitdiff
path: root/client/src/leap/soledad/client/_secrets/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/leap/soledad/client/_secrets/__init__.py')
-rw-r--r--client/src/leap/soledad/client/_secrets/__init__.py132
1 files changed, 132 insertions, 0 deletions
diff --git a/client/src/leap/soledad/client/_secrets/__init__.py b/client/src/leap/soledad/client/_secrets/__init__.py
new file mode 100644
index 00000000..f9da8423
--- /dev/null
+++ b/client/src/leap/soledad/client/_secrets/__init__.py
@@ -0,0 +1,132 @@
+# -*- coding: utf-8 -*-
+# _secrets/__init__.py
+# Copyright (C) 2016 LEAP
+#
+# This program 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.
+#
+# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+from collections import namedtuple
+
+from leap.soledad.common.log import getLogger
+
+from leap.soledad.client._secrets.storage import SecretsStorage
+from leap.soledad.client._secrets.crypto import SecretsCrypto
+from leap.soledad.client._secrets.util import emit
+
+
+logger = getLogger(__name__)
+
+
+SecretLength = namedtuple('SecretLength', 'name length')
+
+
+class Secrets(object):
+
+ lengths = {
+ 'remote': 512,
+ 'salt': 64,
+ 'local': 448,
+ }
+
+ def __init__(self, uuid, passphrase, url, local_path, creds, userid,
+ shared_db=None):
+ self._passphrase = passphrase
+ self._secrets = {}
+ self._user_data = {'uuid': uuid, 'userid': userid}
+ self.crypto = SecretsCrypto(self.get_passphrase)
+ self.storage = SecretsStorage(
+ uuid, self.get_passphrase, url, local_path, creds, userid,
+ shared_db=shared_db)
+ self._bootstrap()
+
+ #
+ # bootstrap
+ #
+
+ def _bootstrap(self):
+ force_storage = False
+
+ # attempt to load secrets from local storage
+ encrypted = self.storage.load_local()
+
+ # if not found, attempt to load secrets from remote storage
+ if not encrypted:
+ encrypted = self.storage.load_remote()
+
+ if not encrypted:
+ # if not found, generate new secrets
+ secrets = self._generate()
+ encrypted = self.crypto.encrypt(secrets)
+ force_storage = True
+ else:
+ # decrypt secrets found either in local or remote storage
+ secrets = self.crypto.decrypt(encrypted)
+
+ self._secrets = secrets
+
+ if encrypted['version'] < self.crypto.VERSION or force_storage:
+ self.storage.save_local(encrypted)
+ self.storage.save_remote(encrypted)
+
+ #
+ # generation
+ #
+
+ @emit('creating')
+ def _generate(self):
+ logger.info("generating new set of secrets...")
+ secrets = {}
+ for name, length in self.lengths.iteritems():
+ secret = os.urandom(length)
+ secrets[name] = secret
+ logger.info("new set of secrets successfully generated")
+ return secrets
+
+ #
+ # crypto
+ #
+
+ def _encrypt(self):
+ # encrypt secrets
+ secrets = self._secrets
+ encrypted = self.crypto.encrypt(secrets)
+ # create the recovery document
+ data = {'secret': encrypted, 'version': 2}
+ return data
+
+ def get_passphrase(self):
+ return self._passphrase.encode('utf-8')
+
+ @property
+ def passphrase(self):
+ return self.get_passphrase()
+
+ def change_passphrase(self, new_passphrase):
+ self._passphrase = new_passphrase
+ encrypted = self.crypto.encrypt(self._secrets)
+ self.storage.save_local(encrypted)
+ self.storage.save_remote(encrypted)
+
+ @property
+ def remote(self):
+ return self._secrets.get('remote')
+
+ @property
+ def salt(self):
+ return self._secrets.get('salt')
+
+ @property
+ def local(self):
+ return self._secrets.get('local')