sync_apply overwrites local password when encrypt-on-disk is true

This commit is contained in:
Lex Berezhny 2019-10-16 09:18:28 -04:00
parent 1075cd9995
commit bcd69f812e
3 changed files with 25 additions and 49 deletions

View file

@ -1618,10 +1618,15 @@ class Daemon(metaclass=JSONRPCServerType):
return hexlify(wallet.hash).decode() return hexlify(wallet.hash).decode()
@requires("wallet", conditions=[WALLET_IS_UNLOCKED]) @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 Apply incoming synchronization data, if provided, and return a sync hash and update wallet data.
an encrypted wallet.
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: Usage:
sync_apply <password> [--data=<data>] [--wallet_id=<wallet_id>] [--blocking] sync_apply <password> [--data=<data>] [--wallet_id=<wallet_id>] [--blocking]

View file

@ -82,10 +82,7 @@ class WalletEncryptionAndSynchronization(CommandTestCase):
daemon = self.daemon daemon = self.daemon
wallet = daemon.wallet_manager.default_wallet wallet = daemon.wallet_manager.default_wallet
self.assertEqual( self.assertEqual(daemon.jsonrpc_wallet_status(), {'is_locked': False, 'is_encrypted': False})
daemon.jsonrpc_wallet_status(),
{'is_locked': False, 'is_encrypted': False}
)
self.assertIsNone(daemon.jsonrpc_preference_get(ENCRYPT_ON_DISK)) self.assertIsNone(daemon.jsonrpc_preference_get(ENCRYPT_ON_DISK))
self.assertWalletEncrypted(wallet.storage.path, False) self.assertWalletEncrypted(wallet.storage.path, False)
@ -97,39 +94,23 @@ class WalletEncryptionAndSynchronization(CommandTestCase):
daemon.jsonrpc_wallet_decrypt() # already not encrypted daemon.jsonrpc_wallet_decrypt() # already not encrypted
daemon.jsonrpc_wallet_encrypt('password') daemon.jsonrpc_wallet_encrypt('password')
self.assertEqual(daemon.jsonrpc_wallet_status(), {'is_locked': False, 'is_encrypted': True})
self.assertEqual( self.assertEqual(daemon.jsonrpc_preference_get(ENCRYPT_ON_DISK), {'encrypt-on-disk': True})
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) self.assertWalletEncrypted(wallet.storage.path, True)
daemon.jsonrpc_wallet_lock() daemon.jsonrpc_wallet_lock()
self.assertEqual(daemon.jsonrpc_wallet_status(), {'is_locked': True, 'is_encrypted': True})
self.assertEqual( # can't sign transactions with locked wallet
daemon.jsonrpc_wallet_status(),
{'is_locked': True, 'is_encrypted': True}
)
with self.assertRaises(error.ComponentStartConditionNotMet): with self.assertRaises(error.ComponentStartConditionNotMet):
await daemon.jsonrpc_channel_create('@foo', '1.0') await daemon.jsonrpc_channel_create('@foo', '1.0')
daemon.jsonrpc_wallet_unlock('password') 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') await daemon.jsonrpc_channel_create('@foo', '1.0')
daemon.jsonrpc_wallet_decrypt() daemon.jsonrpc_wallet_decrypt()
self.assertEqual( self.assertEqual(daemon.jsonrpc_wallet_status(), {'is_locked': False, 'is_encrypted': False})
daemon.jsonrpc_wallet_status(), self.assertEqual(daemon.jsonrpc_preference_get(ENCRYPT_ON_DISK), {'encrypt-on-disk': False})
{'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) self.assertWalletEncrypted(wallet.storage.path, False)
async def test_sync_with_encryption_and_password_change(self): 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 wallet, wallet2 = daemon.wallet_manager.default_wallet, daemon2.wallet_manager.default_wallet
daemon.jsonrpc_wallet_encrypt('password') daemon.jsonrpc_wallet_encrypt('password')
self.assertEqual(wallet.encryption_password, '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)
data = await daemon2.jsonrpc_sync_apply('password2') data = await daemon2.jsonrpc_sync_apply('password2')
with self.assertRaises(ValueError): # wrong password with self.assertRaises(ValueError): # wrong password
await daemon.jsonrpc_sync_apply('password', data=data['data'], blocking=True) await daemon.jsonrpc_sync_apply('password', data=data['data'], blocking=True)
await daemon.jsonrpc_sync_apply('password2', 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_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_preference_get(ENCRYPT_ON_DISK), {'encrypt-on-disk': True})
self.assertWalletEncrypted(wallet.storage.path, True) self.assertWalletEncrypted(wallet.storage.path, True)
# old password is still used # check new password is active
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')
daemon.jsonrpc_wallet_lock() daemon.jsonrpc_wallet_lock()
self.assertFalse(daemon.jsonrpc_wallet_unlock('password')) self.assertFalse(daemon.jsonrpc_wallet_unlock('password'))
self.assertTrue(daemon.jsonrpc_wallet_unlock('password2')) self.assertTrue(daemon.jsonrpc_wallet_unlock('password2'))
# propagate disk encryption to daemon2
data = await daemon.jsonrpc_sync_apply('password2') data = await daemon.jsonrpc_sync_apply('password2')
self.assertEqual(wallet2.encryption_password, None)
await daemon2.jsonrpc_sync_apply('password2', data=data['data'], blocking=True) await daemon2.jsonrpc_sync_apply('password2', data=data['data'], blocking=True)
self.assertEqual(wallet.encryption_password, 'password2')
# wallet2 is now encrypted using new password
self.assertEqual(daemon2.jsonrpc_wallet_status(), {'is_locked': False, 'is_encrypted': True}) 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.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() daemon2.jsonrpc_wallet_lock()
self.assertTrue(daemon2.jsonrpc_wallet_unlock('password2')) self.assertTrue(daemon2.jsonrpc_wallet_unlock('password2'))

View file

@ -178,7 +178,7 @@ class Wallet:
added_accounts = [] added_accounts = []
decrypted_data = self.unpack(password, data) decrypted_data = self.unpack(password, data)
self.preferences.merge(decrypted_data.get('preferences', {})) 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 self.encryption_password = password
for account_dict in decrypted_data['accounts']: for account_dict in decrypted_data['accounts']:
ledger = manager.get_or_create_ledger(account_dict['ledger']) ledger = manager.get_or_create_ledger(account_dict['ledger'])