From bcd69f812e1656e3a8041405ca035d2c97183f5c Mon Sep 17 00:00:00 2001 From: Lex Berezhny Date: Wed, 16 Oct 2019 09:18:28 -0400 Subject: [PATCH] sync_apply overwrites local password when encrypt-on-disk is true --- lbry/lbry/extras/daemon/Daemon.py | 11 +++- .../tests/integration/test_wallet_commands.py | 61 +++++-------------- torba/torba/client/wallet.py | 2 +- 3 files changed, 25 insertions(+), 49 deletions(-) diff --git a/lbry/lbry/extras/daemon/Daemon.py b/lbry/lbry/extras/daemon/Daemon.py index ca1c9fb26..636439bff 100644 --- a/lbry/lbry/extras/daemon/Daemon.py +++ b/lbry/lbry/extras/daemon/Daemon.py @@ -1618,10 +1618,15 @@ class Daemon(metaclass=JSONRPCServerType): return hexlify(wallet.hash).decode() @requires("wallet", conditions=[WALLET_IS_UNLOCKED]) - async def jsonrpc_sync_apply(self, password, data=None, encrypt_password=None, wallet_id=None, blocking=False): + async def jsonrpc_sync_apply(self, password, data=None, wallet_id=None, blocking=False): """ - Apply incoming synchronization data, if provided, and then produce a sync hash and - an encrypted wallet. + Apply incoming synchronization data, if provided, and return a sync hash and update wallet data. + + Wallet must be unlocked to perform this operation. + + If "encrypt-on-disk" preference is True and supplied password is different from local password, + or there is no local password (because local wallet was not encrypted), then the supplied password + will be used for local encryption (overwriting previous local encryption password). Usage: sync_apply [--data=] [--wallet_id=] [--blocking] diff --git a/lbry/tests/integration/test_wallet_commands.py b/lbry/tests/integration/test_wallet_commands.py index 7a3d0062f..06138a3f3 100644 --- a/lbry/tests/integration/test_wallet_commands.py +++ b/lbry/tests/integration/test_wallet_commands.py @@ -82,10 +82,7 @@ class WalletEncryptionAndSynchronization(CommandTestCase): daemon = self.daemon wallet = daemon.wallet_manager.default_wallet - self.assertEqual( - daemon.jsonrpc_wallet_status(), - {'is_locked': False, 'is_encrypted': False} - ) + self.assertEqual(daemon.jsonrpc_wallet_status(), {'is_locked': False, 'is_encrypted': False}) self.assertIsNone(daemon.jsonrpc_preference_get(ENCRYPT_ON_DISK)) self.assertWalletEncrypted(wallet.storage.path, False) @@ -97,39 +94,23 @@ class WalletEncryptionAndSynchronization(CommandTestCase): daemon.jsonrpc_wallet_decrypt() # already not encrypted daemon.jsonrpc_wallet_encrypt('password') - - self.assertEqual( - daemon.jsonrpc_wallet_status(), - {'is_locked': False, 'is_encrypted': True} - ) - self.assertEqual( - daemon.jsonrpc_preference_get(ENCRYPT_ON_DISK), - {'encrypt-on-disk': True} - ) + self.assertEqual(daemon.jsonrpc_wallet_status(), {'is_locked': False, 'is_encrypted': True}) + self.assertEqual(daemon.jsonrpc_preference_get(ENCRYPT_ON_DISK), {'encrypt-on-disk': True}) self.assertWalletEncrypted(wallet.storage.path, True) daemon.jsonrpc_wallet_lock() + self.assertEqual(daemon.jsonrpc_wallet_status(), {'is_locked': True, 'is_encrypted': True}) - self.assertEqual( - daemon.jsonrpc_wallet_status(), - {'is_locked': True, 'is_encrypted': True} - ) - + # can't sign transactions with locked wallet with self.assertRaises(error.ComponentStartConditionNotMet): await daemon.jsonrpc_channel_create('@foo', '1.0') - daemon.jsonrpc_wallet_unlock('password') + self.assertEqual(daemon.jsonrpc_wallet_status(), {'is_locked': False, 'is_encrypted': True}) await daemon.jsonrpc_channel_create('@foo', '1.0') daemon.jsonrpc_wallet_decrypt() - self.assertEqual( - daemon.jsonrpc_wallet_status(), - {'is_locked': False, 'is_encrypted': False} - ) - self.assertEqual( - daemon.jsonrpc_preference_get(ENCRYPT_ON_DISK), - {'encrypt-on-disk': False} - ) + self.assertEqual(daemon.jsonrpc_wallet_status(), {'is_locked': False, 'is_encrypted': False}) + self.assertEqual(daemon.jsonrpc_preference_get(ENCRYPT_ON_DISK), {'encrypt-on-disk': False}) self.assertWalletEncrypted(wallet.storage.path, False) async def test_sync_with_encryption_and_password_change(self): @@ -137,42 +118,32 @@ class WalletEncryptionAndSynchronization(CommandTestCase): wallet, wallet2 = daemon.wallet_manager.default_wallet, daemon2.wallet_manager.default_wallet daemon.jsonrpc_wallet_encrypt('password') - - self.assertEqual(daemon.jsonrpc_wallet_status(), {'is_locked': False, 'is_encrypted': True}) - self.assertEqual(daemon2.jsonrpc_wallet_status(), {'is_locked': False, 'is_encrypted': False}) - self.assertEqual(daemon.jsonrpc_preference_get(ENCRYPT_ON_DISK), {'encrypt-on-disk': True}) - self.assertIsNone(daemon2.jsonrpc_preference_get(ENCRYPT_ON_DISK)) - self.assertWalletEncrypted(wallet.storage.path, True) - self.assertWalletEncrypted(wallet2.storage.path, False) + self.assertEqual(wallet.encryption_password, 'password') data = await daemon2.jsonrpc_sync_apply('password2') with self.assertRaises(ValueError): # wrong password await daemon.jsonrpc_sync_apply('password', data=data['data'], blocking=True) await daemon.jsonrpc_sync_apply('password2', data=data['data'], blocking=True) - # encryption did not change from before sync_apply + # password changed + self.assertEqual(wallet.encryption_password, 'password2') self.assertEqual(daemon.jsonrpc_wallet_status(), {'is_locked': False, 'is_encrypted': True}) self.assertEqual(daemon.jsonrpc_preference_get(ENCRYPT_ON_DISK), {'encrypt-on-disk': True}) self.assertWalletEncrypted(wallet.storage.path, True) - # old password is still used - daemon.jsonrpc_wallet_lock() - self.assertFalse(daemon.jsonrpc_wallet_unlock('password2')) - self.assertTrue(daemon.jsonrpc_wallet_unlock('password')) - - # encrypt using new password - daemon.jsonrpc_wallet_encrypt('password2') + # check new password is active daemon.jsonrpc_wallet_lock() self.assertFalse(daemon.jsonrpc_wallet_unlock('password')) self.assertTrue(daemon.jsonrpc_wallet_unlock('password2')) + # propagate disk encryption to daemon2 data = await daemon.jsonrpc_sync_apply('password2') + self.assertEqual(wallet2.encryption_password, None) await daemon2.jsonrpc_sync_apply('password2', data=data['data'], blocking=True) - - # wallet2 is now encrypted using new password + self.assertEqual(wallet.encryption_password, 'password2') self.assertEqual(daemon2.jsonrpc_wallet_status(), {'is_locked': False, 'is_encrypted': True}) self.assertEqual(daemon2.jsonrpc_preference_get(ENCRYPT_ON_DISK), {'encrypt-on-disk': True}) - self.assertWalletEncrypted(wallet.storage.path, True) + self.assertWalletEncrypted(wallet2.storage.path, True) daemon2.jsonrpc_wallet_lock() self.assertTrue(daemon2.jsonrpc_wallet_unlock('password2')) diff --git a/torba/torba/client/wallet.py b/torba/torba/client/wallet.py index dc94d34d2..df9ccbe3f 100644 --- a/torba/torba/client/wallet.py +++ b/torba/torba/client/wallet.py @@ -178,7 +178,7 @@ class Wallet: added_accounts = [] decrypted_data = self.unpack(password, data) self.preferences.merge(decrypted_data.get('preferences', {})) - if not self.encryption_password and self.preferences.get(ENCRYPT_ON_DISK, False): + if self.preferences.get(ENCRYPT_ON_DISK, False): self.encryption_password = password for account_dict in decrypted_data['accounts']: ledger = manager.get_or_create_ledger(account_dict['ledger'])