From 4268b2e70df83adad3a46c27d2ec400a19446f92 Mon Sep 17 00:00:00 2001 From: Ken Carpenter <62639971+FoundationKen@users.noreply.github.com> Date: Wed, 29 Dec 2021 15:49:13 -0800 Subject: [PATCH] Dev v1.0.8 (#76) * PASS1-82: Delete input PSBT file after successful signing (#59) * PASS1-156: Bitcoin Core causes invalid bitcoin address error (#63) * PASS1-126: Passport crashes when making a microSD backup (#71) This issue was happening only when attempting to backup a device with a multisig configuration that contained multiple different derivation paths instead of all devices in a given configuration having the same derivation path. * PASS1-181: Passport fails to verify multi-sig change addresses in Sparrow (#72) * PASS1-191: Add pairing support for Simple Bitcoin Wallet (#75) Co-authored-by: Corey Lakey Co-authored-by: Corey Lakey --- ports/stm32/boards/Passport/manifest.py | 4 +- .../stm32/boards/Passport/modules/actions.py | 3 +- ports/stm32/boards/Passport/modules/auth.py | 4 +- .../modules/data_codecs/psbt_txn_sampler.py | 2 +- ports/stm32/boards/Passport/modules/export.py | 2 +- ports/stm32/boards/Passport/modules/files.py | 38 +++++++++++++++++++ ports/stm32/boards/Passport/modules/utils.py | 3 +- .../modules/wallets/simple_bitcoin_wallet.py | 21 ++++++++++ .../Passport/modules/wallets/sw_wallets.py | 2 + 9 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 ports/stm32/boards/Passport/modules/wallets/simple_bitcoin_wallet.py diff --git a/ports/stm32/boards/Passport/manifest.py b/ports/stm32/boards/Passport/manifest.py index 771fa357..e0e0cac7 100644 --- a/ports/stm32/boards/Passport/manifest.py +++ b/ports/stm32/boards/Passport/manifest.py @@ -11,7 +11,7 @@ 'menu.py', 'settings.py', 'sram4.py', 'sffile.py', 'collections/deque.py', 'uQR.py', 'constants.py', 'callgate.py', 'pincodes.py', 'stash.py', 'login_ux.py', 'public_constants.py', 'seed.py', 'chains.py', 'opcodes.py', 'bip39_utils.py', 'seed_entry_ux.py', 'sflash.py', 'snake.py', 'stacking_sats.py', - 'se_commands.py', 'serializations.py','seed_check_ux.py', 'export.py', 'compat7z.py', 'multisig.py', 'psbt.py', + 'se_commands.py', 'serializations.py', 'seed_check_ux.py', 'export.py', 'compat7z.py', 'multisig.py', 'psbt.py', 'periodic.py', 'exceptions.py', 'noise_source.py', 'self_test_ux.py', 'flash_cache.py', 'history.py', 'accounts.py', 'log.py', 'descriptor.py', 'accept_terms_ux.py', 'new_wallet.py', 'stat.py', 'uasyncio/__init__.py', 'uasyncio/core.py', 'uasyncio/queues.py', 'uasyncio/synchro.py', 'ie.py', @@ -33,4 +33,4 @@ 'wallets/multisig_json.py', 'wallets/multisig_import.py', 'wallets/generic_json_wallet.py', 'wallets/sparrow.py', 'wallets/bitcoin_core.py', 'wallets/wasabi.py', 'wallets/btcpay.py', 'wallets/gordian.py', 'wallets/lily.py', 'wallets/fullynoded.py', 'wallets/dux_reserve.py', 'wallets/specter.py', 'wallets/casa.py', 'wallets/vault.py', - 'wallets/caravan.py')) + 'wallets/caravan.py', 'wallets/simple_bitcoin_wallet.py')) diff --git a/ports/stm32/boards/Passport/modules/actions.py b/ports/stm32/boards/Passport/modules/actions.py index 5f060952..ec65c96b 100644 --- a/ports/stm32/boards/Passport/modules/actions.py +++ b/ports/stm32/boards/Passport/modules/actions.py @@ -21,7 +21,7 @@ import common from common import settings, system, noise, dis from utils import (UXStateMachine, imported, pretty_short_delay, xfp2str, to_str, - truncate_string_to_width, set_next_addr, scan_for_address, get_accounts, run_chooser, + truncate_string_to_width, set_next_addr, get_accounts, run_chooser, make_account_name_num, is_valid_address, save_next_addr, needs_microsd, format_btc_address, is_all_zero, bytes_to_hex_str, split_to_lines, is_valid_btc_address, do_address_verify, run_chooser) from wallets.utils import get_export_mode, get_addr_type_from_address, get_deriv_path_from_addr_type_and_acct @@ -272,6 +272,7 @@ async def show(self): if address == None: return + address = address.lower() address, is_valid_btc = await is_valid_btc_address(address) if is_valid_btc == False: if not self.goto_prev(): diff --git a/ports/stm32/boards/Passport/modules/auth.py b/ports/stm32/boards/Passport/modules/auth.py index f21d7d33..cfabe58d 100644 --- a/ports/stm32/boards/Passport/modules/auth.py +++ b/ports/stm32/boards/Passport/modules/auth.py @@ -684,7 +684,7 @@ def sign_transaction(psbt_len, flags=0x0, psbt_sha=None): def sign_psbt_file(filename): # sign a PSBT file found on a microSD card - from files import CardSlot, CardMissingError + from files import CardSlot, CardMissingError, securely_blank_file from common import dis, system # from sram4 import tmp_buf -- the fd.readinto() below doesn't work for some odd reason, even though the fd.readinto() for firmware updates tmp_buf = bytearray(1024) @@ -798,6 +798,8 @@ async def done(psbt): # save transaction, in hex txid = psbt.finalize(fd) + securely_blank_file(filename) + # success and done! break diff --git a/ports/stm32/boards/Passport/modules/data_codecs/psbt_txn_sampler.py b/ports/stm32/boards/Passport/modules/data_codecs/psbt_txn_sampler.py index 01fd213c..52f0923e 100644 --- a/ports/stm32/boards/Passport/modules/data_codecs/psbt_txn_sampler.py +++ b/ports/stm32/boards/Passport/modules/data_codecs/psbt_txn_sampler.py @@ -15,7 +15,7 @@ class PsbtTxnSampler(DataSampler): # Return True if it matches or False if not. @classmethod def sample(cls, data): - print('psbt sampler: data={}'.format(b2a_hex(data))) + # print('psbt sampler: data={}'.format(b2a_hex(data))) if data[0:5] == b'psbt\xff': return True if data[0:10] == b'70736274ff': # hex-encoded diff --git a/ports/stm32/boards/Passport/modules/export.py b/ports/stm32/boards/Passport/modules/export.py index 074ff216..7b2515ae 100644 --- a/ports/stm32/boards/Passport/modules/export.py +++ b/ports/stm32/boards/Passport/modules/export.py @@ -40,7 +40,7 @@ def ms_has_master_xfp(xpubs): master_xfp = settings.get('xfp', None) for xpub in xpubs: - (xfp, _) = xpub + xfp = xpub[0] # print('ms_has_master_xfp: xfp={} master_xfp={}'.format(xfp, master_xfp)) if xfp == master_xfp: # print('Including this one') diff --git a/ports/stm32/boards/Passport/modules/files.py b/ports/stm32/boards/Passport/modules/files.py index 879ab7e1..2a3467d7 100644 --- a/ports/stm32/boards/Passport/modules/files.py +++ b/ports/stm32/boards/Passport/modules/files.py @@ -246,4 +246,42 @@ def get_file_path(self, filename, path=None): return fname, basename+ext +def securely_blank_file(full_path): + # input PSBT file no longer required; so delete it + # - blank with zeros + # - rename to garbage (to hide filename after undelete) + # - delete + # - ok if file missing already (card maybe have been swapped) + # + # NOTE: we know the FAT filesystem code is simple, see + # ../external/micropython/extmod/vfs_fat.[ch] + + path, basename = full_path.rsplit('/', 1) + + with CardSlot() as card: + try: + blk = bytes(64) + + with open(full_path, 'r+b') as fd: + size = fd.seek(0, 2) + fd.seek(0) + + # blank it + for i in range((size // len(blk)) + 1): + fd.write(blk) + + assert fd.seek(0, 1) >= size + + # probably pointless, but why not: + os.sync() + + except OSError as exc: + # missing file is okay + if exc.args[0] == ENOENT: return + raise + + # rename it and delete + new_name = path + '/' + ('x'*len(basename)) + os.rename(full_path, new_name) + os.remove(new_name) # EOF diff --git a/ports/stm32/boards/Passport/modules/utils.py b/ports/stm32/boards/Passport/modules/utils.py index 52d6a8dd..846bdd59 100644 --- a/ports/stm32/boards/Passport/modules/utils.py +++ b/ports/stm32/boards/Passport/modules/utils.py @@ -447,6 +447,7 @@ async def show_top_menu(): # TODO: For now this just checks the front bytes, but it could ensure the whole thing is valid def is_valid_address(address): # Valid addresses: 1 , 3 , bc1, tb1, m, n, 2 + address = address.lower() return (len(address) > 3) and \ ((address[0] == '1') or \ (address[0] == '2') or \ @@ -530,7 +531,7 @@ def find_address(path, start_address_idx, address, addr_type, ms_wallet, is_chan with stash.SensitiveValues() as sv: if ms_wallet: # NOTE: Can't easily reverse order here, so this is slightly less efficient - for (curr_idx, paths, curr_address, script) in ms_wallet.yield_addresses(start_address_idx, max_to_check): + for (curr_idx, paths, curr_address, script) in ms_wallet.yield_addresses(start_address_idx, max_to_check, change_idx=1 if is_change else 0): # print('curr_idx={}: paths={} curr_address = {}'.format(curr_idx, paths, curr_address)) if curr_address == address: diff --git a/ports/stm32/boards/Passport/modules/wallets/simple_bitcoin_wallet.py b/ports/stm32/boards/Passport/modules/wallets/simple_bitcoin_wallet.py new file mode 100644 index 00000000..c5126f7f --- /dev/null +++ b/ports/stm32/boards/Passport/modules/wallets/simple_bitcoin_wallet.py @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2021 Foundation Devices, Inc. +# SPDX-License-Identifier: GPL-3.0-or-later +# +# simple_bitcoin_wallet.py - Simple Bitcoin Wallet support +# + +from .generic_json_wallet import create_generic_json_wallet +from .multisig_json import create_multisig_json_wallet +from .multisig_import import read_multisig_config_from_qr, read_multisig_config_from_microsd +from data_codecs.qr_type import QRType + +SimpleBitcoinWallet = { + 'label': 'Simple Bitcoin Wallet', + 'sig_types': [ + {'id': 'single-sig', 'label': 'Single-sig', 'addr_type': None, + 'create_wallet': create_generic_json_wallet}, + ], + 'export_modes': [ + {'id': 'qr', 'label': 'QR Code', 'qr_type': QRType.UR2}, + ] +} diff --git a/ports/stm32/boards/Passport/modules/wallets/sw_wallets.py b/ports/stm32/boards/Passport/modules/wallets/sw_wallets.py index b75a3b37..a42cfca7 100644 --- a/ports/stm32/boards/Passport/modules/wallets/sw_wallets.py +++ b/ports/stm32/boards/Passport/modules/wallets/sw_wallets.py @@ -15,6 +15,7 @@ # from .fullynoded import FullyNodedWallet # from .gordian import GordianWallet # from .lily import LilyWallet +from .simple_bitcoin_wallet import SimpleBitcoinWallet from .sparrow import SparrowWallet from .specter import SpecterWallet from .wasabi import WasabiWallet @@ -31,6 +32,7 @@ # FullyNodedWallet, # GordianWallet, # LilyWallet, + SimpleBitcoinWallet, SparrowWallet, SpecterWallet, WasabiWallet,