mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-01 09:45:18 +00:00
qt PasswordLineEdit: try to clear password from memory
If an attacker has access to the process' memory, it's probably already game over, still we can make their life a bit harder. I really tried but failed to encapsulate this logic inside PasswordLineEdit. The destroyed signal arrives too late. deleteLater is not called. __del__ gets called too late.
This commit is contained in:
parent
c798e5d9a1
commit
5259fcb6fd
3 changed files with 78 additions and 52 deletions
|
@ -281,51 +281,57 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
||||||
name_e.textChanged.connect(on_filename)
|
name_e.textChanged.connect(on_filename)
|
||||||
name_e.setText(os.path.basename(path))
|
name_e.setText(os.path.basename(path))
|
||||||
|
|
||||||
while True:
|
def run_user_interaction_loop():
|
||||||
if self.loop.exec_() != 2: # 2 = next
|
while True:
|
||||||
raise UserCancelled
|
if self.loop.exec_() != 2: # 2 = next
|
||||||
assert temp_storage
|
raise UserCancelled
|
||||||
if temp_storage.file_exists() and not temp_storage.is_encrypted():
|
assert temp_storage
|
||||||
break
|
if temp_storage.file_exists() and not temp_storage.is_encrypted():
|
||||||
if not temp_storage.file_exists():
|
break
|
||||||
break
|
if not temp_storage.file_exists():
|
||||||
wallet_from_memory = get_wallet_from_daemon(temp_storage.path)
|
break
|
||||||
if wallet_from_memory:
|
wallet_from_memory = get_wallet_from_daemon(temp_storage.path)
|
||||||
raise WalletAlreadyOpenInMemory(wallet_from_memory)
|
if wallet_from_memory:
|
||||||
if temp_storage.file_exists() and temp_storage.is_encrypted():
|
raise WalletAlreadyOpenInMemory(wallet_from_memory)
|
||||||
if temp_storage.is_encrypted_with_user_pw():
|
if temp_storage.file_exists() and temp_storage.is_encrypted():
|
||||||
password = pw_e.text()
|
if temp_storage.is_encrypted_with_user_pw():
|
||||||
try:
|
password = pw_e.text()
|
||||||
temp_storage.decrypt(password)
|
try:
|
||||||
break
|
temp_storage.decrypt(password)
|
||||||
except InvalidPassword as e:
|
break
|
||||||
self.show_message(title=_('Error'), msg=str(e))
|
except InvalidPassword as e:
|
||||||
continue
|
self.show_message(title=_('Error'), msg=str(e))
|
||||||
except BaseException as e:
|
continue
|
||||||
self.logger.exception('')
|
except BaseException as e:
|
||||||
self.show_message(title=_('Error'), msg=repr(e))
|
self.logger.exception('')
|
||||||
raise UserCancelled()
|
self.show_message(title=_('Error'), msg=repr(e))
|
||||||
elif temp_storage.is_encrypted_with_hw_device():
|
raise UserCancelled()
|
||||||
try:
|
elif temp_storage.is_encrypted_with_hw_device():
|
||||||
self.run('choose_hw_device', HWD_SETUP_DECRYPT_WALLET, storage=temp_storage)
|
try:
|
||||||
except InvalidPassword as e:
|
self.run('choose_hw_device', HWD_SETUP_DECRYPT_WALLET, storage=temp_storage)
|
||||||
self.show_message(title=_('Error'),
|
except InvalidPassword as e:
|
||||||
msg=_('Failed to decrypt using this hardware device.') + '\n' +
|
self.show_message(title=_('Error'),
|
||||||
_('If you use a passphrase, make sure it is correct.'))
|
msg=_('Failed to decrypt using this hardware device.') + '\n' +
|
||||||
self.reset_stack()
|
_('If you use a passphrase, make sure it is correct.'))
|
||||||
return self.select_storage(path, get_wallet_from_daemon)
|
self.reset_stack()
|
||||||
except (UserCancelled, GoBack):
|
return self.select_storage(path, get_wallet_from_daemon)
|
||||||
raise
|
except (UserCancelled, GoBack):
|
||||||
except BaseException as e:
|
raise
|
||||||
self.logger.exception('')
|
except BaseException as e:
|
||||||
self.show_message(title=_('Error'), msg=repr(e))
|
self.logger.exception('')
|
||||||
raise UserCancelled()
|
self.show_message(title=_('Error'), msg=repr(e))
|
||||||
if temp_storage.is_past_initial_decryption():
|
raise UserCancelled()
|
||||||
break
|
if temp_storage.is_past_initial_decryption():
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise UserCancelled()
|
||||||
else:
|
else:
|
||||||
raise UserCancelled()
|
raise Exception('Unexpected encryption version')
|
||||||
else:
|
|
||||||
raise Exception('Unexpected encryption version')
|
try:
|
||||||
|
run_user_interaction_loop()
|
||||||
|
finally:
|
||||||
|
pw_e.clear()
|
||||||
|
|
||||||
return temp_storage.path, (temp_storage if temp_storage.file_exists() else None)
|
return temp_storage.path, (temp_storage if temp_storage.file_exists() else None)
|
||||||
|
|
||||||
|
@ -482,8 +488,11 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
||||||
playout = PasswordLayout(msg=msg, kind=kind, OK_button=self.next_button,
|
playout = PasswordLayout(msg=msg, kind=kind, OK_button=self.next_button,
|
||||||
force_disable_encrypt_cb=force_disable_encrypt_cb)
|
force_disable_encrypt_cb=force_disable_encrypt_cb)
|
||||||
playout.encrypt_cb.setChecked(True)
|
playout.encrypt_cb.setChecked(True)
|
||||||
self.exec_layout(playout.layout())
|
try:
|
||||||
return playout.new_password(), playout.encrypt_cb.isChecked()
|
self.exec_layout(playout.layout())
|
||||||
|
return playout.new_password(), playout.encrypt_cb.isChecked()
|
||||||
|
finally:
|
||||||
|
playout.clear_password_fields()
|
||||||
|
|
||||||
@wizard_dialog
|
@wizard_dialog
|
||||||
def request_password(self, run_next, force_disable_encrypt_cb=False):
|
def request_password(self, run_next, force_disable_encrypt_cb=False):
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import math
|
import math
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtGui import QPixmap
|
from PyQt5.QtGui import QPixmap
|
||||||
|
@ -165,6 +166,10 @@ class PasswordLayout(object):
|
||||||
pw = None
|
pw = None
|
||||||
return pw
|
return pw
|
||||||
|
|
||||||
|
def clear_password_fields(self):
|
||||||
|
for field in [self.pw, self.new_pw, self.conf_pw]:
|
||||||
|
field.clear()
|
||||||
|
|
||||||
|
|
||||||
class PasswordLayoutForHW(object):
|
class PasswordLayoutForHW(object):
|
||||||
|
|
||||||
|
@ -258,9 +263,12 @@ class ChangePasswordDialogForSW(ChangePasswordDialogBase):
|
||||||
force_disable_encrypt_cb=not wallet.can_have_keystore_encryption())
|
force_disable_encrypt_cb=not wallet.can_have_keystore_encryption())
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if not self.exec_():
|
try:
|
||||||
return False, None, None, None
|
if not self.exec_():
|
||||||
return True, self.playout.old_password(), self.playout.new_password(), self.playout.encrypt_cb.isChecked()
|
return False, None, None, None
|
||||||
|
return True, self.playout.old_password(), self.playout.new_password(), self.playout.encrypt_cb.isChecked()
|
||||||
|
finally:
|
||||||
|
self.playout.clear_password_fields()
|
||||||
|
|
||||||
|
|
||||||
class ChangePasswordDialogForHW(ChangePasswordDialogBase):
|
class ChangePasswordDialogForHW(ChangePasswordDialogBase):
|
||||||
|
@ -301,6 +309,9 @@ class PasswordDialog(WindowModalDialog):
|
||||||
run_hook('password_dialog', pw, grid, 1)
|
run_hook('password_dialog', pw, grid, 1)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if not self.exec_():
|
try:
|
||||||
return
|
if not self.exec_():
|
||||||
return self.pw.text()
|
return
|
||||||
|
return self.pw.text()
|
||||||
|
finally:
|
||||||
|
self.pw.clear()
|
||||||
|
|
|
@ -753,6 +753,12 @@ class PasswordLineEdit(QLineEdit):
|
||||||
QLineEdit.__init__(self, *args, **kwargs)
|
QLineEdit.__init__(self, *args, **kwargs)
|
||||||
self.setEchoMode(QLineEdit.Password)
|
self.setEchoMode(QLineEdit.Password)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
# Try to actually overwrite the memory.
|
||||||
|
# This is really just a best-effort thing...
|
||||||
|
self.setText(len(self.text()) * " ")
|
||||||
|
super().clear()
|
||||||
|
|
||||||
|
|
||||||
class TaskThread(QThread):
|
class TaskThread(QThread):
|
||||||
'''Thread that runs background tasks. Callbacks are guaranteed
|
'''Thread that runs background tasks. Callbacks are guaranteed
|
||||||
|
|
Loading…
Add table
Reference in a new issue