Beispiel #1
0
    def _put_secrets_in_shared_db(self):
        """
        Assert local keys are the same as shared db's ones.

        Try to fetch keys from shared recovery database. If they already exist
        in the remote db, assert that that data is the same as local data.
        Otherwise, upload keys to shared recovery database.
        """
        soledad_assert(
            self._has_secret(),
            'Tried to send keys to server but they don\'t exist in local '
            'storage.')
        # try to get secrets doc from server, otherwise create it
        doc = self._get_secrets_from_shared_db()
        if doc is None:
            doc = SoledadDocument(
                doc_id=self._shared_db_doc_id())
        # fill doc with encrypted secrets
        doc.content = self.export_recovery_document()
        # upload secrets to server
        signal(SOLEDAD_UPLOADING_KEYS, self._uuid)
        db = self._shared_db
        if not db:
            logger.warning('No shared db found')
            return
        db.put_doc(doc)
        signal(SOLEDAD_DONE_UPLOADING_KEYS, self._uuid)
Beispiel #2
0
    def _put_secrets_in_shared_db(self):
        """
        Assert local keys are the same as shared db's ones.

        Try to fetch keys from shared recovery database. If they already exist
        in the remote db, assert that that data is the same as local data.
        Otherwise, upload keys to shared recovery database.
        """
        soledad_assert(
            self._has_secret(),
            'Tried to send keys to server but they don\'t exist in local '
            'storage.')
        # try to get secrets doc from server, otherwise create it
        doc = self._get_secrets_from_shared_db()
        if doc is None:
            doc = SoledadDocument(doc_id=self._shared_db_doc_id())
        # fill doc with encrypted secrets
        doc.content = self.export_recovery_document()
        # upload secrets to server
        signal(SOLEDAD_UPLOADING_KEYS, self._uuid)
        db = self._shared_db
        if not db:
            logger.warning('No shared db found')
            return
        db.put_doc(doc)
        signal(SOLEDAD_DONE_UPLOADING_KEYS, self._uuid)
Beispiel #3
0
    def sync(self, defer_decryption=True):
        """
        Synchronize the local encrypted replica with a remote replica.

        This method blocks until a syncing lock is acquired, so there are no
        attempts of concurrent syncs from the same client replica.

        :param url: the url of the target replica to sync with
        :type url: str

        :param defer_decryption: Whether to defer the decryption process using
                                 the intermediate database. If False,
                                 decryption will be done inline.
        :type defer_decryption: bool

        :return: The local generation before the synchronisation was
                 performed.
        :rtype: str
        """
        if self._db:
            try:
                local_gen = self._db.sync(urlparse.urljoin(
                    self.server_url, 'user-%s' % self._uuid),
                                          creds=self._creds,
                                          autocreate=False,
                                          defer_decryption=defer_decryption)
                signal(SOLEDAD_DONE_DATA_SYNC, self._uuid)
                return local_gen
            except Exception as e:
                logger.error("Soledad exception when syncing: %s" % str(e))
Beispiel #4
0
    def sync(self, defer_decryption=True):
        """
        Synchronize the local encrypted replica with a remote replica.

        This method blocks until a syncing lock is acquired, so there are no
        attempts of concurrent syncs from the same client replica.

        :param url: the url of the target replica to sync with
        :type url: str

        :param defer_decryption: Whether to defer the decryption process using
                                 the intermediate database. If False,
                                 decryption will be done inline.
        :type defer_decryption: bool

        :return: The local generation before the synchronisation was
                 performed.
        :rtype: str
        """
        if self._db:
            try:
                local_gen = self._db.sync(
                    urlparse.urljoin(self.server_url, 'user-%s' % self._uuid),
                    creds=self._creds, autocreate=False,
                    defer_decryption=defer_decryption)
                signal(SOLEDAD_DONE_DATA_SYNC, self._uuid)
                return local_gen
            except Exception as e:
                logger.error("Soledad exception when syncing: %s" % str(e))
Beispiel #5
0
    def _gen_secret(self):
        """
        Generate a secret for symmetric encryption and store in a local
        encrypted file.

        This method emits the following signals:

            * SOLEDAD_CREATING_KEYS
            * SOLEDAD_DONE_CREATING_KEYS

        A secret has the following structure:

            {
                '<secret_id>': {
                        'kdf': 'scrypt',
                        'kdf_salt': '<b64 repr of salt>'
                        'kdf_length': <key length>
                        'cipher': 'aes256',
                        'length': <secret length>,
                        'secret': '<encrypted b64 repr of storage_secret>',
                }
            }

        :return: The id of the generated secret.
        :rtype: str
        """
        signal(SOLEDAD_CREATING_KEYS, self._uuid)
        # generate random secret
        secret = os.urandom(self.GENERATED_SECRET_LENGTH)
        secret_id = sha256(secret).hexdigest()
        # generate random salt
        salt = os.urandom(self.SALT_LENGTH)
        # get a 256-bit key
        key = scrypt.hash(self._passphrase_as_string(), salt, buflen=32)
        iv, ciphertext = self._crypto.encrypt_sym(secret, key)
        self._secrets[secret_id] = {
            # leap.soledad.crypto submodule uses AES256 for symmetric
            # encryption.
            self.KDF_KEY:
            self.KDF_SCRYPT,
            self.KDF_SALT_KEY:
            binascii.b2a_base64(salt),
            self.KDF_LENGTH_KEY:
            len(key),
            self.CIPHER_KEY:
            self.CIPHER_AES256,
            self.LENGTH_KEY:
            len(secret),
            self.SECRET_KEY:
            '%s%s%s' %
            (str(iv), self.IV_SEPARATOR, binascii.b2a_base64(ciphertext)),
        }
        self._store_secrets()
        signal(SOLEDAD_DONE_CREATING_KEYS, self._uuid)
        return secret_id
def patched_sync(self, defer_decryption=True):
    if self._db:
        try:
            local_gen = self._db.sync(
                urlparse.urljoin(self.server_url, 'user-%s' % self._uuid),
                creds=self._creds, autocreate=False,
                defer_decryption=defer_decryption)
            signal(SOLEDAD_DONE_DATA_SYNC, self._uuid)
            return local_gen
        except Exception as e:
            client.logger.error("Soledad exception when syncing: %s - %s" % (e.__class__.__name__, e.message))
Beispiel #7
0
def patched_sync(self, defer_decryption=True):
    if self._db:
        try:
            local_gen = self._db.sync(
                urlparse.urljoin(self.server_url, 'user-%s' % self._uuid),
                creds=self._creds, autocreate=False,
                defer_decryption=defer_decryption)
            signal(SOLEDAD_DONE_DATA_SYNC, self._uuid)
            return local_gen
        except Exception as e:
            client.logger.error("Soledad exception when syncing: %s - %s" % (e.__class__.__name__, e.message))
Beispiel #8
0
    def _gen_secret(self):
        """
        Generate a secret for symmetric encryption and store in a local
        encrypted file.

        This method emits the following signals:

            * SOLEDAD_CREATING_KEYS
            * SOLEDAD_DONE_CREATING_KEYS

        A secret has the following structure:

            {
                '<secret_id>': {
                        'kdf': 'scrypt',
                        'kdf_salt': '<b64 repr of salt>'
                        'kdf_length': <key length>
                        'cipher': 'aes256',
                        'length': <secret length>,
                        'secret': '<encrypted b64 repr of storage_secret>',
                }
            }

        :return: The id of the generated secret.
        :rtype: str
        """
        signal(SOLEDAD_CREATING_KEYS, self._uuid)
        # generate random secret
        secret = os.urandom(self.GENERATED_SECRET_LENGTH)
        secret_id = sha256(secret).hexdigest()
        # generate random salt
        salt = os.urandom(self.SALT_LENGTH)
        # get a 256-bit key
        key = scrypt.hash(self._passphrase_as_string(), salt, buflen=32)
        iv, ciphertext = self._crypto.encrypt_sym(secret, key)
        self._secrets[secret_id] = {
            # leap.soledad.crypto submodule uses AES256 for symmetric
            # encryption.
            self.KDF_KEY: self.KDF_SCRYPT,
            self.KDF_SALT_KEY: binascii.b2a_base64(salt),
            self.KDF_LENGTH_KEY: len(key),
            self.CIPHER_KEY: self.CIPHER_AES256,
            self.LENGTH_KEY: len(secret),
            self.SECRET_KEY: '%s%s%s' % (
                str(iv), self.IV_SEPARATOR, binascii.b2a_base64(ciphertext)),
        }
        self._store_secrets()
        signal(SOLEDAD_DONE_CREATING_KEYS, self._uuid)
        return secret_id
Beispiel #9
0
    def _get_secrets_from_shared_db(self):
        """
        Retrieve the document with encrypted key material from the shared
        database.

        :return: a document with encrypted key material in its contents
        :rtype: SoledadDocument
        """
        signal(SOLEDAD_DOWNLOADING_KEYS, self._uuid)
        db = self._shared_db
        if not db:
            logger.warning('No shared db found')
            return
        doc = db.get_doc(self._shared_db_doc_id())
        signal(SOLEDAD_DONE_DOWNLOADING_KEYS, self._uuid)
        return doc
Beispiel #10
0
    def _get_secrets_from_shared_db(self):
        """
        Retrieve the document with encrypted key material from the shared
        database.

        :return: a document with encrypted key material in its contents
        :rtype: SoledadDocument
        """
        signal(SOLEDAD_DOWNLOADING_KEYS, self._uuid)
        db = self._shared_db
        if not db:
            logger.warning('No shared db found')
            return
        doc = db.get_doc(self._shared_db_doc_id())
        signal(SOLEDAD_DONE_DOWNLOADING_KEYS, self._uuid)
        return doc
Beispiel #11
0
    def need_sync(self, url):
        """
        Return if local db replica differs from remote url's replica.

        :param url: The remote replica to compare with local replica.
        :type url: str

        :return: Whether remote replica and local replica differ.
        :rtype: bool
        """
        target = SoledadSyncTarget(
            url, self._db._get_replica_uid(), creds=self._creds,
            crypto=self._crypto)
        info = target.get_sync_info(self._db._get_replica_uid())
        # compare source generation with target's last known source generation
        if self._db._get_generation() != info[4]:
            signal(SOLEDAD_NEW_DATA_TO_SYNC, self._uuid)
            return True
        return False
Beispiel #12
0
    def need_sync(self, url):
        """
        Return if local db replica differs from remote url's replica.

        :param url: The remote replica to compare with local replica.
        :type url: str

        :return: Whether remote replica and local replica differ.
        :rtype: bool
        """
        target = SoledadSyncTarget(url,
                                   self._db._get_replica_uid(),
                                   creds=self._creds,
                                   crypto=self._crypto)
        info = target.get_sync_info(self._db._get_replica_uid())
        # compare source generation with target's last known source generation
        if self._db._get_generation() != info[4]:
            signal(SOLEDAD_NEW_DATA_TO_SYNC, self._uuid)
            return True
        return False