Merge pull request #3346 from SomberNight/encrypt_watch_only_wallets
allow encrypting watch-only wallets
This commit is contained in:
@@ -24,12 +24,19 @@
|
||||
# SOFTWARE.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from . import bitcoin
|
||||
from . import keystore
|
||||
from .keystore import bip44_derivation
|
||||
from .wallet import Imported_Wallet, Standard_Wallet, Multisig_Wallet, wallet_types
|
||||
from .storage import STO_EV_USER_PW, STO_EV_XPUB_PW, get_derivation_used_for_hw_device_encryption
|
||||
from .i18n import _
|
||||
from .util import UserCancelled
|
||||
|
||||
# hardware device setup purpose
|
||||
HWD_SETUP_NEW_WALLET, HWD_SETUP_DECRYPT_WALLET = range(0, 2)
|
||||
|
||||
class ScriptTypeNotSupported(Exception): pass
|
||||
|
||||
@@ -147,17 +154,22 @@ class BaseWizard(object):
|
||||
is_valid=v, allow_multi=True)
|
||||
|
||||
def on_import(self, text):
|
||||
# create a temporary wallet and exploit that modifications
|
||||
# will be reflected on self.storage
|
||||
if keystore.is_address_list(text):
|
||||
self.wallet = Imported_Wallet(self.storage)
|
||||
w = Imported_Wallet(self.storage)
|
||||
for x in text.split():
|
||||
self.wallet.import_address(x)
|
||||
w.import_address(x)
|
||||
elif keystore.is_private_key_list(text):
|
||||
k = keystore.Imported_KeyStore({})
|
||||
self.storage.put('keystore', k.dump())
|
||||
self.wallet = Imported_Wallet(self.storage)
|
||||
w = Imported_Wallet(self.storage)
|
||||
for x in text.split():
|
||||
self.wallet.import_private_key(x, None)
|
||||
self.terminate()
|
||||
w.import_private_key(x, None)
|
||||
self.keystores.append(w.keystore)
|
||||
else:
|
||||
return self.terminate()
|
||||
return self.run('create_wallet')
|
||||
|
||||
def restore_from_key(self):
|
||||
if self.wallet_type == 'standard':
|
||||
@@ -176,7 +188,7 @@ class BaseWizard(object):
|
||||
k = keystore.from_master_key(text)
|
||||
self.on_keystore(k)
|
||||
|
||||
def choose_hw_device(self):
|
||||
def choose_hw_device(self, purpose=HWD_SETUP_NEW_WALLET):
|
||||
title = _('Hardware Keystore')
|
||||
# check available plugins
|
||||
support = self.plugins.get_hardware_support()
|
||||
@@ -185,7 +197,7 @@ class BaseWizard(object):
|
||||
_('No hardware wallet support found on your system.'),
|
||||
_('Please install the relevant libraries (eg python-trezor for Trezor).'),
|
||||
])
|
||||
self.confirm_dialog(title=title, message=msg, run_next= lambda x: self.choose_hw_device())
|
||||
self.confirm_dialog(title=title, message=msg, run_next= lambda x: self.choose_hw_device(purpose))
|
||||
return
|
||||
# scan devices
|
||||
devices = []
|
||||
@@ -205,7 +217,7 @@ class BaseWizard(object):
|
||||
_('If your device is not detected on Windows, go to "Settings", "Devices", "Connected devices", and do "Remove device". Then, plug your device again.') + ' ',
|
||||
_('On Linux, you might have to add a new permission to your udev rules.'),
|
||||
])
|
||||
self.confirm_dialog(title=title, message=msg, run_next= lambda x: self.choose_hw_device())
|
||||
self.confirm_dialog(title=title, message=msg, run_next= lambda x: self.choose_hw_device(purpose))
|
||||
return
|
||||
# select device
|
||||
self.devices = devices
|
||||
@@ -216,23 +228,31 @@ class BaseWizard(object):
|
||||
descr = "%s [%s, %s]" % (label, name, state)
|
||||
choices.append(((name, info), descr))
|
||||
msg = _('Select a device') + ':'
|
||||
self.choice_dialog(title=title, message=msg, choices=choices, run_next=self.on_device)
|
||||
self.choice_dialog(title=title, message=msg, choices=choices, run_next= lambda *args: self.on_device(*args, purpose=purpose))
|
||||
|
||||
def on_device(self, name, device_info):
|
||||
def on_device(self, name, device_info, *, purpose):
|
||||
self.plugin = self.plugins.get_plugin(name)
|
||||
try:
|
||||
self.plugin.setup_device(device_info, self)
|
||||
self.plugin.setup_device(device_info, self, purpose)
|
||||
except BaseException as e:
|
||||
self.show_error(str(e))
|
||||
self.choose_hw_device()
|
||||
self.choose_hw_device(purpose)
|
||||
return
|
||||
if self.wallet_type=='multisig':
|
||||
# There is no general standard for HD multisig.
|
||||
# This is partially compatible with BIP45; assumes index=0
|
||||
self.on_hw_derivation(name, device_info, "m/45'/0")
|
||||
if purpose == HWD_SETUP_NEW_WALLET:
|
||||
if self.wallet_type=='multisig':
|
||||
# There is no general standard for HD multisig.
|
||||
# This is partially compatible with BIP45; assumes index=0
|
||||
self.on_hw_derivation(name, device_info, "m/45'/0")
|
||||
else:
|
||||
f = lambda x: self.run('on_hw_derivation', name, device_info, str(x))
|
||||
self.derivation_dialog(f)
|
||||
elif purpose == HWD_SETUP_DECRYPT_WALLET:
|
||||
derivation = get_derivation_used_for_hw_device_encryption()
|
||||
xpub = self.plugin.get_xpub(device_info.device.id_, derivation, 'standard', self)
|
||||
password = keystore.Xpub.get_pubkey_from_xpub(xpub, ())
|
||||
self.storage.decrypt(password)
|
||||
else:
|
||||
f = lambda x: self.run('on_hw_derivation', name, device_info, str(x))
|
||||
self.derivation_dialog(f)
|
||||
raise Exception('unknown purpose: %s' % purpose)
|
||||
|
||||
def derivation_dialog(self, f):
|
||||
default = bip44_derivation(0, bip43_purpose=44)
|
||||
@@ -365,13 +385,45 @@ class BaseWizard(object):
|
||||
self.run('create_wallet')
|
||||
|
||||
def create_wallet(self):
|
||||
if any(k.may_have_password() for k in self.keystores):
|
||||
self.request_password(run_next=self.on_password)
|
||||
encrypt_keystore = any(k.may_have_password() for k in self.keystores)
|
||||
# note: the following condition ("if") is duplicated logic from
|
||||
# wallet.get_available_storage_encryption_version()
|
||||
if self.wallet_type == 'standard' and isinstance(self.keystores[0], keystore.Hardware_KeyStore):
|
||||
# offer encrypting with a pw derived from the hw device
|
||||
k = self.keystores[0]
|
||||
try:
|
||||
k.handler = self.plugin.create_handler(self)
|
||||
password = k.get_password_for_storage_encryption()
|
||||
except UserCancelled:
|
||||
devmgr = self.plugins.device_manager
|
||||
devmgr.unpair_xpub(k.xpub)
|
||||
self.choose_hw_device()
|
||||
return
|
||||
except BaseException as e:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
self.show_error(str(e))
|
||||
return
|
||||
self.request_storage_encryption(
|
||||
run_next=lambda encrypt_storage: self.on_password(
|
||||
password,
|
||||
encrypt_storage=encrypt_storage,
|
||||
storage_enc_version=STO_EV_XPUB_PW,
|
||||
encrypt_keystore=False))
|
||||
else:
|
||||
self.on_password(None, False)
|
||||
# prompt the user to set an arbitrary password
|
||||
self.request_password(
|
||||
run_next=lambda password, encrypt_storage: self.on_password(
|
||||
password,
|
||||
encrypt_storage=encrypt_storage,
|
||||
storage_enc_version=STO_EV_USER_PW,
|
||||
encrypt_keystore=encrypt_keystore),
|
||||
force_disable_encrypt_cb=not encrypt_keystore)
|
||||
|
||||
def on_password(self, password, encrypt):
|
||||
self.storage.set_password(password, encrypt)
|
||||
def on_password(self, password, *, encrypt_storage,
|
||||
storage_enc_version=STO_EV_USER_PW, encrypt_keystore):
|
||||
self.storage.set_keystore_encryption(bool(password) and encrypt_keystore)
|
||||
if encrypt_storage:
|
||||
self.storage.set_password(password, enc_version=storage_enc_version)
|
||||
for k in self.keystores:
|
||||
if k.may_have_password():
|
||||
k.update_password(None, password)
|
||||
@@ -387,6 +439,13 @@ class BaseWizard(object):
|
||||
self.storage.write()
|
||||
self.wallet = Multisig_Wallet(self.storage)
|
||||
self.run('create_addresses')
|
||||
elif self.wallet_type == 'imported':
|
||||
if len(self.keystores) > 0:
|
||||
keys = self.keystores[0].dump()
|
||||
self.storage.put('keystore', keys)
|
||||
self.wallet = Imported_Wallet(self.storage)
|
||||
self.wallet.storage.write()
|
||||
self.terminate()
|
||||
|
||||
def show_xpub_and_add_cosigners(self, xpub):
|
||||
self.show_xpub_dialog(xpub=xpub, run_next=lambda x: self.run('choose_keystore'))
|
||||
|
||||
@@ -643,8 +643,8 @@ def verify_message(address, sig, message):
|
||||
return False
|
||||
|
||||
|
||||
def encrypt_message(message, pubkey):
|
||||
return EC_KEY.encrypt_message(message, bfh(pubkey))
|
||||
def encrypt_message(message, pubkey, magic=b'BIE1'):
|
||||
return EC_KEY.encrypt_message(message, bfh(pubkey), magic)
|
||||
|
||||
|
||||
def chunks(l, n):
|
||||
@@ -789,7 +789,7 @@ class EC_KEY(object):
|
||||
# ECIES encryption/decryption methods; AES-128-CBC with PKCS7 is used as the cipher; hmac-sha256 is used as the mac
|
||||
|
||||
@classmethod
|
||||
def encrypt_message(self, message, pubkey):
|
||||
def encrypt_message(self, message, pubkey, magic=b'BIE1'):
|
||||
assert_bytes(message)
|
||||
|
||||
pk = ser_to_point(pubkey)
|
||||
@@ -803,20 +803,20 @@ class EC_KEY(object):
|
||||
iv, key_e, key_m = key[0:16], key[16:32], key[32:]
|
||||
ciphertext = aes_encrypt_with_iv(key_e, iv, message)
|
||||
ephemeral_pubkey = bfh(ephemeral.get_public_key(compressed=True))
|
||||
encrypted = b'BIE1' + ephemeral_pubkey + ciphertext
|
||||
encrypted = magic + ephemeral_pubkey + ciphertext
|
||||
mac = hmac.new(key_m, encrypted, hashlib.sha256).digest()
|
||||
|
||||
return base64.b64encode(encrypted + mac)
|
||||
|
||||
def decrypt_message(self, encrypted):
|
||||
def decrypt_message(self, encrypted, magic=b'BIE1'):
|
||||
encrypted = base64.b64decode(encrypted)
|
||||
if len(encrypted) < 85:
|
||||
raise Exception('invalid ciphertext: length')
|
||||
magic = encrypted[:4]
|
||||
magic_found = encrypted[:4]
|
||||
ephemeral_pubkey = encrypted[4:37]
|
||||
ciphertext = encrypted[37:-32]
|
||||
mac = encrypted[-32:]
|
||||
if magic != b'BIE1':
|
||||
if magic_found != magic:
|
||||
raise Exception('invalid ciphertext: invalid magic bytes')
|
||||
try:
|
||||
ephemeral_pubkey = ser_to_point(ephemeral_pubkey)
|
||||
|
||||
@@ -82,7 +82,7 @@ def command(s):
|
||||
password = kwargs.get('password')
|
||||
if c.requires_wallet and wallet is None:
|
||||
raise BaseException("wallet not loaded. Use 'electrum daemon load_wallet'")
|
||||
if c.requires_password and password is None and wallet.storage.get('use_encryption'):
|
||||
if c.requires_password and password is None and wallet.has_password():
|
||||
return {'error': 'Password required' }
|
||||
return func(*args, **kwargs)
|
||||
return func_wrapper
|
||||
|
||||
@@ -45,6 +45,10 @@ class KeyStore(PrintError):
|
||||
def can_import(self):
|
||||
return False
|
||||
|
||||
def may_have_password(self):
|
||||
"""Returns whether the keystore can be encrypted with a password."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_tx_derivations(self, tx):
|
||||
keypairs = {}
|
||||
for txin in tx.inputs():
|
||||
@@ -116,9 +120,6 @@ class Imported_KeyStore(Software_KeyStore):
|
||||
def is_deterministic(self):
|
||||
return False
|
||||
|
||||
def can_change_password(self):
|
||||
return True
|
||||
|
||||
def get_master_public_key(self):
|
||||
return None
|
||||
|
||||
@@ -196,9 +197,6 @@ class Deterministic_KeyStore(Software_KeyStore):
|
||||
def is_watching_only(self):
|
||||
return not self.has_seed()
|
||||
|
||||
def can_change_password(self):
|
||||
return not self.is_watching_only()
|
||||
|
||||
def add_seed(self, seed):
|
||||
if self.seed:
|
||||
raise Exception("a seed exists")
|
||||
@@ -522,9 +520,13 @@ class Hardware_KeyStore(KeyStore, Xpub):
|
||||
assert not self.has_seed()
|
||||
return False
|
||||
|
||||
def can_change_password(self):
|
||||
return False
|
||||
|
||||
def get_password_for_storage_encryption(self):
|
||||
from .storage import get_derivation_used_for_hw_device_encryption
|
||||
client = self.plugin.get_client(self)
|
||||
derivation = get_derivation_used_for_hw_device_encryption()
|
||||
xpub = client.get_xpub(derivation, "standard")
|
||||
password = self.get_pubkey_from_xpub(xpub, ())
|
||||
return password
|
||||
|
||||
|
||||
def bip39_normalize_passphrase(passphrase):
|
||||
|
||||
@@ -33,7 +33,7 @@ import pbkdf2, hmac, hashlib
|
||||
import base64
|
||||
import zlib
|
||||
|
||||
from .util import PrintError, profiler
|
||||
from .util import PrintError, profiler, InvalidPassword
|
||||
from .plugins import run_hook, plugin_loaders
|
||||
from .keystore import bip44_derivation
|
||||
from . import bitcoin
|
||||
@@ -56,6 +56,13 @@ def multisig_type(wallet_type):
|
||||
match = [int(x) for x in match.group(1, 2)]
|
||||
return match
|
||||
|
||||
def get_derivation_used_for_hw_device_encryption():
|
||||
return ("m"
|
||||
"/4541509'" # ascii 'ELE' as decimal ("BIP43 purpose")
|
||||
"/1112098098'") # ascii 'BIE2' as decimal
|
||||
|
||||
# storage encryption version
|
||||
STO_EV_PLAINTEXT, STO_EV_USER_PW, STO_EV_XPUB_PW = range(0, 3)
|
||||
|
||||
class WalletStorage(PrintError):
|
||||
|
||||
@@ -70,9 +77,11 @@ class WalletStorage(PrintError):
|
||||
if self.file_exists():
|
||||
with open(self.path, "r") as f:
|
||||
self.raw = f.read()
|
||||
self._encryption_version = self._init_encryption_version()
|
||||
if not self.is_encrypted():
|
||||
self.load_data(self.raw)
|
||||
else:
|
||||
self._encryption_version = STO_EV_PLAINTEXT
|
||||
# avoid new wallets getting 'upgraded'
|
||||
self.put('seed_version', FINAL_SEED_VERSION)
|
||||
|
||||
@@ -106,11 +115,47 @@ class WalletStorage(PrintError):
|
||||
if self.requires_upgrade():
|
||||
self.upgrade()
|
||||
|
||||
def is_past_initial_decryption(self):
|
||||
"""Return if storage is in a usable state for normal operations.
|
||||
|
||||
The value is True exactly
|
||||
if encryption is disabled completely (self.is_encrypted() == False),
|
||||
or if encryption is enabled but the contents have already been decrypted.
|
||||
"""
|
||||
return bool(self.data)
|
||||
|
||||
def is_encrypted(self):
|
||||
"""Return if storage encryption is currently enabled."""
|
||||
return self.get_encryption_version() != STO_EV_PLAINTEXT
|
||||
|
||||
def is_encrypted_with_user_pw(self):
|
||||
return self.get_encryption_version() == STO_EV_USER_PW
|
||||
|
||||
def is_encrypted_with_hw_device(self):
|
||||
return self.get_encryption_version() == STO_EV_XPUB_PW
|
||||
|
||||
def get_encryption_version(self):
|
||||
"""Return the version of encryption used for this storage.
|
||||
|
||||
0: plaintext / no encryption
|
||||
|
||||
ECIES, private key derived from a password,
|
||||
1: password is provided by user
|
||||
2: password is derived from an xpub; used with hw wallets
|
||||
"""
|
||||
return self._encryption_version
|
||||
|
||||
def _init_encryption_version(self):
|
||||
try:
|
||||
return base64.b64decode(self.raw)[0:4] == b'BIE1'
|
||||
magic = base64.b64decode(self.raw)[0:4]
|
||||
if magic == b'BIE1':
|
||||
return STO_EV_USER_PW
|
||||
elif magic == b'BIE2':
|
||||
return STO_EV_XPUB_PW
|
||||
else:
|
||||
return STO_EV_PLAINTEXT
|
||||
except:
|
||||
return False
|
||||
return STO_EV_PLAINTEXT
|
||||
|
||||
def file_exists(self):
|
||||
return self.path and os.path.exists(self.path)
|
||||
@@ -120,20 +165,50 @@ class WalletStorage(PrintError):
|
||||
ec_key = bitcoin.EC_KEY(secret)
|
||||
return ec_key
|
||||
|
||||
def _get_encryption_magic(self):
|
||||
v = self._encryption_version
|
||||
if v == STO_EV_USER_PW:
|
||||
return b'BIE1'
|
||||
elif v == STO_EV_XPUB_PW:
|
||||
return b'BIE2'
|
||||
else:
|
||||
raise Exception('no encryption magic for version: %s' % v)
|
||||
|
||||
def decrypt(self, password):
|
||||
ec_key = self.get_key(password)
|
||||
s = zlib.decompress(ec_key.decrypt_message(self.raw)) if self.raw else None
|
||||
if self.raw:
|
||||
enc_magic = self._get_encryption_magic()
|
||||
s = zlib.decompress(ec_key.decrypt_message(self.raw, enc_magic))
|
||||
else:
|
||||
s = None
|
||||
self.pubkey = ec_key.get_public_key()
|
||||
s = s.decode('utf8')
|
||||
self.load_data(s)
|
||||
|
||||
def set_password(self, password, encrypt):
|
||||
self.put('use_encryption', bool(password))
|
||||
if encrypt and password:
|
||||
def check_password(self, password):
|
||||
"""Raises an InvalidPassword exception on invalid password"""
|
||||
if not self.is_encrypted():
|
||||
return
|
||||
if self.pubkey and self.pubkey != self.get_key(password).get_public_key():
|
||||
raise InvalidPassword()
|
||||
|
||||
def set_keystore_encryption(self, enable):
|
||||
self.put('use_encryption', enable)
|
||||
|
||||
def set_password(self, password, enc_version=None):
|
||||
"""Set a password to be used for encrypting this storage."""
|
||||
if enc_version is None:
|
||||
enc_version = self._encryption_version
|
||||
if password and enc_version != STO_EV_PLAINTEXT:
|
||||
ec_key = self.get_key(password)
|
||||
self.pubkey = ec_key.get_public_key()
|
||||
self._encryption_version = enc_version
|
||||
else:
|
||||
self.pubkey = None
|
||||
self._encryption_version = STO_EV_PLAINTEXT
|
||||
# make sure next storage.write() saves changes
|
||||
with self.lock:
|
||||
self.modified = True
|
||||
|
||||
def get(self, key, default=None):
|
||||
with self.lock:
|
||||
@@ -175,7 +250,8 @@ class WalletStorage(PrintError):
|
||||
if self.pubkey:
|
||||
s = bytes(s, 'utf8')
|
||||
c = zlib.compress(s)
|
||||
s = bitcoin.encrypt_message(c, self.pubkey)
|
||||
enc_magic = self._get_encryption_magic()
|
||||
s = bitcoin.encrypt_message(c, self.pubkey, enc_magic)
|
||||
s = s.decode('utf8')
|
||||
|
||||
temp_path = "%s.tmp.%s" % (self.path, os.getpid())
|
||||
|
||||
102
lib/wallet.py
102
lib/wallet.py
@@ -48,7 +48,7 @@ from .util import (NotEnoughFunds, PrintError, UserCancelled, profiler,
|
||||
from .bitcoin import *
|
||||
from .version import *
|
||||
from .keystore import load_keystore, Hardware_KeyStore
|
||||
from .storage import multisig_type
|
||||
from .storage import multisig_type, STO_EV_PLAINTEXT, STO_EV_USER_PW, STO_EV_XPUB_PW
|
||||
|
||||
from . import transaction
|
||||
from .transaction import Transaction
|
||||
@@ -1383,10 +1383,65 @@ class Abstract_Wallet(PrintError):
|
||||
self.synchronizer.add(address)
|
||||
|
||||
def has_password(self):
|
||||
return self.storage.get('use_encryption', False)
|
||||
return self.has_keystore_encryption() or self.has_storage_encryption()
|
||||
|
||||
def can_have_keystore_encryption(self):
|
||||
return self.keystore and self.keystore.may_have_password()
|
||||
|
||||
def get_available_storage_encryption_version(self):
|
||||
"""Returns the type of storage encryption offered to the user.
|
||||
|
||||
A wallet file (storage) is either encrypted with this version
|
||||
or is stored in plaintext.
|
||||
"""
|
||||
if isinstance(self.keystore, Hardware_KeyStore):
|
||||
return STO_EV_XPUB_PW
|
||||
else:
|
||||
return STO_EV_USER_PW
|
||||
|
||||
def has_keystore_encryption(self):
|
||||
"""Returns whether encryption is enabled for the keystore.
|
||||
|
||||
If True, e.g. signing a transaction will require a password.
|
||||
"""
|
||||
if self.can_have_keystore_encryption():
|
||||
return self.storage.get('use_encryption', False)
|
||||
return False
|
||||
|
||||
def has_storage_encryption(self):
|
||||
"""Returns whether encryption is enabled for the wallet file on disk."""
|
||||
return self.storage.is_encrypted()
|
||||
|
||||
@classmethod
|
||||
def may_have_password(cls):
|
||||
return True
|
||||
|
||||
def check_password(self, password):
|
||||
self.keystore.check_password(password)
|
||||
if self.has_keystore_encryption():
|
||||
self.keystore.check_password(password)
|
||||
self.storage.check_password(password)
|
||||
|
||||
def update_password(self, old_pw, new_pw, encrypt_storage=False):
|
||||
if old_pw is None and self.has_password():
|
||||
raise InvalidPassword()
|
||||
self.check_password(old_pw)
|
||||
|
||||
if encrypt_storage:
|
||||
enc_version = self.get_available_storage_encryption_version()
|
||||
else:
|
||||
enc_version = STO_EV_PLAINTEXT
|
||||
self.storage.set_password(new_pw, enc_version)
|
||||
|
||||
# note: Encrypting storage with a hw device is currently only
|
||||
# allowed for non-multisig wallets. Further,
|
||||
# Hardware_KeyStore.may_have_password() == False.
|
||||
# If these were not the case,
|
||||
# extra care would need to be taken when encrypting keystores.
|
||||
self._update_password_for_keystore(old_pw, new_pw)
|
||||
encrypt_keystore = self.can_have_keystore_encryption()
|
||||
self.storage.set_keystore_encryption(bool(new_pw) and encrypt_keystore)
|
||||
|
||||
self.storage.write()
|
||||
|
||||
def sign_message(self, address, message, password):
|
||||
index = self.get_address_index(address)
|
||||
@@ -1420,16 +1475,10 @@ class Simple_Wallet(Abstract_Wallet):
|
||||
def is_watching_only(self):
|
||||
return self.keystore.is_watching_only()
|
||||
|
||||
def can_change_password(self):
|
||||
return self.keystore.can_change_password()
|
||||
|
||||
def update_password(self, old_pw, new_pw, encrypt=False):
|
||||
if old_pw is None and self.has_password():
|
||||
raise InvalidPassword()
|
||||
self.keystore.update_password(old_pw, new_pw)
|
||||
self.save_keystore()
|
||||
self.storage.set_password(new_pw, encrypt)
|
||||
self.storage.write()
|
||||
def _update_password_for_keystore(self, old_pw, new_pw):
|
||||
if self.keystore and self.keystore.may_have_password():
|
||||
self.keystore.update_password(old_pw, new_pw)
|
||||
self.save_keystore()
|
||||
|
||||
def save_keystore(self):
|
||||
self.storage.put('keystore', self.keystore.dump())
|
||||
@@ -1468,9 +1517,6 @@ class Imported_Wallet(Simple_Wallet):
|
||||
def save_addresses(self):
|
||||
self.storage.put('addresses', self.addresses)
|
||||
|
||||
def can_change_password(self):
|
||||
return not self.is_watching_only()
|
||||
|
||||
def can_import_address(self):
|
||||
return self.is_watching_only()
|
||||
|
||||
@@ -1849,22 +1895,28 @@ class Multisig_Wallet(Deterministic_Wallet):
|
||||
def get_keystores(self):
|
||||
return [self.keystores[i] for i in sorted(self.keystores.keys())]
|
||||
|
||||
def update_password(self, old_pw, new_pw, encrypt=False):
|
||||
if old_pw is None and self.has_password():
|
||||
raise InvalidPassword()
|
||||
def can_have_keystore_encryption(self):
|
||||
return any([k.may_have_password() for k in self.get_keystores()])
|
||||
|
||||
def _update_password_for_keystore(self, old_pw, new_pw):
|
||||
for name, keystore in self.keystores.items():
|
||||
if keystore.can_change_password():
|
||||
if keystore.may_have_password():
|
||||
keystore.update_password(old_pw, new_pw)
|
||||
self.storage.put(name, keystore.dump())
|
||||
self.storage.set_password(new_pw, encrypt)
|
||||
self.storage.write()
|
||||
|
||||
def check_password(self, password):
|
||||
for name, keystore in self.keystores.items():
|
||||
if keystore.may_have_password():
|
||||
keystore.check_password(password)
|
||||
self.storage.check_password(password)
|
||||
|
||||
def get_available_storage_encryption_version(self):
|
||||
# multisig wallets are not offered hw device encryption
|
||||
return STO_EV_USER_PW
|
||||
|
||||
def has_seed(self):
|
||||
return self.keystore.has_seed()
|
||||
|
||||
def can_change_password(self):
|
||||
return self.keystore.can_change_password()
|
||||
|
||||
def is_watching_only(self):
|
||||
return not any([not k.is_watching_only() for k in self.get_keystores()])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user