예제 #1
0
def generate_generic_export(account_num=0):
    # Generate data that other programers will use to import Coldcard (single-signer)
    from public_constants import AF_CLASSIC, AF_P2WPKH, AF_P2WPKH_P2SH

    chain = chains.current_chain()

    rv = dict(chain=chain.ctype,
                xpub = settings.get('xpub'),
                xfp = xfp2str(settings.get('xfp')),
                account = account_num,
            )

    with stash.SensitiveValues() as sv:
        # each of these paths would have /{change}/{idx} in usage (not hardened)
        for name, deriv, fmt, atype in [
            ( 'bip44', "m/44'/{ct}'/{acc}'", AF_CLASSIC, 'p2pkh' ),
            ( 'bip49', "m/49'/{ct}'/{acc}'", AF_P2WPKH_P2SH, 'p2sh-p2wpkh' ),   # was "p2wpkh-p2sh"
            ( 'bip84', "m/84'/{ct}'/{acc}'", AF_P2WPKH, 'p2wpkh' ),
        ]:
            dd = deriv.format(ct=chain.b44_cointype, acc=account_num)
            node = sv.derive_path(dd)
            xfp = xfp2str(swab32(node.my_fp()))
            xp = chain.serialize_public(node, AF_CLASSIC)
            zp = chain.serialize_public(node, fmt) if fmt != AF_CLASSIC else None

            # bonus/check: first non-change address: 0/0
            node.derive(0, False).derive(0, False)

            rv[name] = dict(deriv=dd, xpub=xp, xfp=xfp, first=chain.address(node, fmt), name=atype)
            if zp:
                rv[name]['_pub'] = zp

    return rv
예제 #2
0
    def check_xpub(cls, xfp, xpub, expect_chain, xpubs, path_tops):
        # Shared code: consider an xpub for inclusion into a wallet, if ok, append
        # to list: xpubs, and path_tops

        try:
            # Note: addr fmt detected here via SLIP-132 isn't useful
            node, chain, _ = import_xpub(xpub)
        except:
            print(xpub)
            raise AssertionError('unable to parse xpub')

        assert node.private_key() == None, 'no private keys plz'
        assert chain.ctype == expect_chain, 'wrong chain, expect: ' + expect_chain

        # NOTE: could enforce all same depth, and/or all depth >= 1, but
        # seems like more restrictive than needed.
        if node.depth() == 1:
            if not xfp:
                # allow a shortcut: zero/omit xfp => use observed parent value
                xfp = swab32(node.fingerprint())
            else:
                # generally cannot check fingerprint values, but if we can, do.
                assert swab32(node.fingerprint()) == xfp, 'xfp depth=1 wrong'

        assert xfp, 'need fingerprint'

        # detect, when possible, if it follows BIP45 ... find the path
        path_top = None
        if node.depth() == 1:
            cn = node.child_num()
            path_top = str(cn & 0x7fffffff)
            if cn & 0x80000000:
                path_top += "'"

        path_tops.add(path_top)

        # serialize xpub w/ BIP32 standard now.
        # - this has effect of stripping SLIP-132 confusion away
        xpubs.append((xfp, chain.serialize_public(node, AF_P2SH)))

        return xfp
예제 #3
0
파일: stash.py 프로젝트: ramsemune/firmware
    def capture_xpub(self):
        # track my xpubkey fingerprint & value in settings (not sensitive really)
        # - we share these on any USB connection
        from nvstore import settings

        # Implicit in the values is the BIP-39 encryption passphrase,
        # which we might not want to actually store.
        xfp = swab32(self.node.my_fp())
        xpub = self.chain.serialize_public(self.node)

        if self._bip39pw:
            settings.put_volatile('xfp', xfp)
            settings.put_volatile('xpub', xpub)
        else:
            settings.overrides.clear()
            settings.put('xfp', xfp)
            settings.put('xpub', xpub)

        settings.put('chain', self.chain.ctype)
        settings.put('words', (self.mode == 'words'))
예제 #4
0
def generate_public_contents():
    # Generate public details about wallet.
    #
    # simple text format: 
    #   key = value
    # or #comments
    # but value is JSON
    from public_constants import AF_CLASSIC

    num_rx = 5

    chain = chains.current_chain()

    with stash.SensitiveValues() as sv:

        xfp = xfp2str(swab32(sv.node.my_fp()))

        yield ('''\
# Coldcard Wallet Summary File
## For wallet with master key fingerprint: {xfp}

Wallet operates on blockchain: {nb}

For BIP-44, this is coin_type '{ct}', and internally we use
symbol {sym} for this blockchain.

## IMPORTANT WARNING

Do **not** deposit to any address in this file unless you have a working
wallet system that is ready to handle the funds at that address!

## Top-level, 'master' extended public key ('m/'):

{xpub}

What follows are derived public keys and payment addresses, as may
be needed for different systems.


'''.format(nb=chain.name, xpub=chain.serialize_public(sv.node), 
            sym=chain.ctype, ct=chain.b44_cointype, xfp=xfp))

        for name, path, addr_fmt in chains.CommonDerivations:

            if '{coin_type}' in path:
                path = path.replace('{coin_type}', str(chain.b44_cointype))

            if '{' in name:
                name = name.format(core_name=chain.core_name)

            show_slip132 = ('Core' not in name)

            yield ('''## For {name}: {path}\n\n'''.format(name=name, path=path))
            yield ('''First %d receive addresses (account=0, change=0):\n\n''' % num_rx)

            submaster = None
            for i in range(num_rx):
                subpath = path.format(account=0, change=0, idx=i)

                # find the prefix of the path that is hardneded
                if "'" in subpath:
                    hard_sub = subpath.rsplit("'", 1)[0] + "'"
                else:
                    hard_sub = 'm'

                if hard_sub != submaster:
                    # dump the xpub needed

                    if submaster:
                        yield "\n"

                    node = sv.derive_path(hard_sub, register=False)
                    yield ("%s => %s\n" % (hard_sub, chain.serialize_public(node)))
                    if show_slip132 and addr_fmt != AF_CLASSIC and (addr_fmt in chain.slip132):
                        yield ("%s => %s   ##SLIP-132##\n" % (
                                    hard_sub, chain.serialize_public(node, addr_fmt)))

                    submaster = hard_sub
                    node.blank()
                    del node

                # show the payment address
                node = sv.derive_path(subpath, register=False)
                yield ('%s => %s\n' % (subpath, chain.address(node, addr_fmt)))

                node.blank()
                del node

            yield ('\n\n')

    from multisig import MultisigWallet
    if MultisigWallet.exists():
        yield '\n# Your Multisig Wallets\n\n'
        from uio import StringIO

        for ms in MultisigWallet.get_all():
            fp = StringIO()

            ms.render_export(fp)
            print("\n---\n", file=fp)

            yield fp.getvalue()
            del fp
    def check_xpub(cls, xfp, xpub, deriv, expect_chain, my_xfp, xpubs):
        # Shared code: consider an xpub for inclusion into a wallet, if ok, append
        # to list: xpubs with a tuple: (xfp, deriv, xpub)
        # return T if it's our own key
        # - deriv can be None, and in very limited cases can recover derivation path
        # - could enforce all same depth, and/or all depth >= 1, but
        #   seems like more restrictive than needed, so "m" is allowed

        try:
            # Note: addr fmt detected here via SLIP-132 isn't useful
            node, chain, _ = import_xpub(xpub)
        except:
            raise AssertionError('unable to parse xpub')

        assert node.private_key() == None       # 'no privkeys plz'
        assert chain.ctype == expect_chain      # 'wrong chain'

        depth = node.depth()

        if depth == 1:
            if not xfp:
                # allow a shortcut: zero/omit xfp => use observed parent value
                xfp = swab32(node.fingerprint())
            else:
                # generally cannot check fingerprint values, but if we can, do so.
                assert swab32(node.fingerprint()) == xfp, 'xfp depth=1 wrong'

        assert xfp, 'need fingerprint'          # happens if bare xpub given

        # In most cases, we cannot verify the derivation path because it's hardened
        # and we know none of the private keys involved.
        if depth == 1:
            # but derivation is implied at depth==1
            guess = keypath_to_str([node.child_num()], skip=0)

            if deriv:
                assert guess == deriv, '%s != %s' % (guess, deriv)
            else:
                deriv = guess           # reachable? doubt it

        assert deriv, 'empty deriv'         # or force to be 'm'?

        # path length of derivation given needs to match xpub's depth
        assert deriv[0] == 'm'
        p_len = deriv.count('/')
        assert p_len == depth, 'deriv %d != %d xpub depth (xfp=%s)' % (
                                        p_len, depth, xfp2str(xfp))

        if xfp == my_xfp:
            # its supposed to be my key, so I should be able to generate pubkey
            # - might indicate collision on xfp value between co-signers,
            #   and that's not supported
            with stash.SensitiveValues() as sv:
                chk_node = sv.derive_path(deriv)
                assert node.public_key() == chk_node.public_key(), \
                            "(m=%s)/%s wrong pubkey" % (xfp2str(xfp), deriv[2:])

        # serialize xpub w/ BIP32 standard now.
        # - this has effect of stripping SLIP-132 confusion away
        xpubs.append((xfp, deriv, chain.serialize_public(node, AF_P2SH)))

        return (xfp == my_xfp)
예제 #6
0
from pincodes import pa
from nvstore import settings
from stash import SecretStash, SensitiveValues
from utils import xfp2str, swab32

tn = chains.BitcoinTestnet

b32_version_pub = 0x043587cf
b32_version_priv = 0x04358394

node = ngu.hdnode.HDNode()
v = node.deserialize(main.TPRV)
assert v == b32_version_priv
assert node

if settings.get('xfp') == swab32(node.my_fp()):
    print("right xfp already")

else:
    settings.current = sim_defaults
    settings.overrides.clear()
    settings.set('chain', 'XTN')

    raw = SecretStash.encode(xprv=node)
    pa.change(new_secret=raw)
    pa.new_main_secret(raw)

    print("New key in effect: %s" % settings.get('xpub', 'MISSING'))
    print("Fingerprint: %s" % xfp2str(settings.get('xfp', 0)))

    assert settings.get('xfp', 0) == swab32(node.my_fp())