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)
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)
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))
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))
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))
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 _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
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
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