Esempio n. 1
0
    def p2sh_address(cls, addr_fmt, witdeem_script):
        # Multisig and general P2SH support
        # - witdeem => witness script for segwit, or redeem script otherwise
        # - redeem script can be generated from witness script if needed.
        # - this function needs a witdeem script to be provided, not simple to make
        # - more verification needed to prove it's change/included address (NOT HERE)
        # - reference: <https://bitcoincore.org/en/segwit_wallet_dev/>
        # - returns: str(address)

        assert addr_fmt & AFC_SCRIPT, 'for p2sh only'
        assert witdeem_script, "need witness/redeem script"

        if addr_fmt & AFC_SEGWIT:
            digest = tcc.sha256(witdeem_script).digest()
        else:
            digest = hash160(witdeem_script)

        if addr_fmt & AFC_BECH32:
            # bech32 encoded segwit p2sh
            addr = tcc.codecs.bech32_encode(cls.bech32_hrp, 0, digest)
        elif addr_fmt == AF_P2WSH_P2SH:
            # segwit p2wsh encoded as classic P2SH
            addr = tcc.codecs.b58_encode(cls.b58_script +
                                         hash160(b'\x00\x20' + digest))
        else:
            # P2SH classic
            addr = tcc.codecs.b58_encode(cls.b58_script + digest)

        return addr
Esempio n. 2
0
    def address(cls, node, addr_fmt):
        # return a human-readable, properly formatted address

        if addr_fmt == AF_CLASSIC:
            # olde fashioned P2PKH
            assert len(cls.b58_addr) == 1
            return node.address(cls.b58_addr[0])

        if addr_fmt & AFC_SCRIPT:
            # use p2sh_address() instead.
            raise ValueError(hex(addr_fmt))

        # so must be P2PKH, fetch it.
        assert addr_fmt & AFC_PUBKEY
        raw = node.address_raw()
        assert len(raw) == 20

        if addr_fmt & AFC_BECH32:
            # bech32 encoded segwit p2pkh
            return tcc.codecs.bech32_encode(cls.bech32_hrp, 0, raw)

        # see bip-141, "P2WPKH nested in BIP16 P2SH" section
        assert addr_fmt == AF_P2WPKH_P2SH
        assert len(cls.b58_script) == 1
        digest = hash160(b'\x00\x14' + raw)

        return tcc.codecs.b58_encode(cls.b58_script + digest)
Esempio n. 3
0
    def address(cls, node, addr_fmt):
        # return a spending address

        if addr_fmt == AF_CLASSIC:
            # olde fashioned P2PKH
            assert len(cls.b58_addr) == 1
            return node.address(cls.b58_addr[0])

        if addr_fmt & AFC_SCRIPT:
            # TODO: no multisig support yet (wrapped segwit doesn't count)
            # - we'd need more info than we have here anyway
            raise ValueError(hex(addr_fmt))

        # so must be P2PKH, fetch it.
        assert addr_fmt & AFC_PUBKEY
        raw = node.address_raw()
        assert len(raw) == 20

        if addr_fmt & AFC_BECH32:
            # bech32 encoded segwit p2pkh
            return tcc.codecs.bech32_encode(cls.bech32_hrp, 0, raw)

        # see bip-141, "P2WPKH nested in BIP16 P2SH" section
        assert addr_fmt == AF_P2WPKH_P2SH
        assert len(cls.b58_script) == 1
        digest = hash160(b'\x00\x14' + raw)

        return tcc.codecs.b58_encode(cls.b58_script + digest)
Esempio n. 4
0
    def determine_my_signing_key(self, utxo):
        # See what it takes to sign this particular input
        # - type of script
        # - which pubkey needed
        # - scriptSig value
        addr_type, addr_or_pubkey, self.is_segwit = utxo.get_address()

        which_key = None
        self.is_multisig = False
        self.is_p2sh = False
        self.amount = utxo.nValue

        if addr_type == 'p2sh':
            # multisig input
            self.is_p2sh = True

            # we must have the redeem script already (else fail)
            if not self.redeem_script:
                raise AssertionError("missing redeem script for in #%d" %
                                     self.my_index)

            redeem_script = self.get(self.redeem_script)
            self.scriptSig = ser_string(redeem_script)

            # new cheat: psbt creator probably telling us exactly what key
            # to use, by providing exactly one. This is ideal for p2sh wrapped p2pkh
            if len(self.subpaths) == 1:
                which_key, = self.subpaths.keys()
            else:
                # messy P2SH multisig guessing?
                ws = self.get(self.witness_script
                              ) if self.witness_script else redeem_script
                for pubkey in self.subpaths:
                    if pubkey in ws:
                        # limitations:
                        # - we could be holding multiple legs of the P2SH
                        # - text match like this could be fooled w/ crafting
                        which_key = pubkey
                        break

            if not self.is_segwit and \
                    len(redeem_script) == 22 and \
                    redeem_script[0] == 0 and redeem_script[1] == 20:
                # it's actually segwit p2pkh inside p2sh
                addr_type = 'p2wpkh-p2sh'
                addr = redeem_script[2:22]
                self.is_segwit = True
            else:
                # multiple keys involved, we probably can't do the finalize step
                self.is_multisig = True

        elif addr_type == 'p2pkh':
            # input is hash160 of a single public key
            self.scriptSig = utxo.scriptPubKey
            addr = addr_or_pubkey

            for pubkey in self.subpaths:
                if hash160(pubkey) == addr:
                    which_key = pubkey
                    break

        elif addr_type == 'p2pk':
            # input is single public key (less common)
            self.scriptSig = utxo.scriptPubKey
            assert len(addr_or_pubkey) == 33

            if addr_or_pubkey in self.subpaths:
                which_key = addr_or_pubkey

        else:
            # we don't know how to "solve" this type of input
            pass

        if not which_key:
            print(
                "no key: input #%d: type=%s segwit=%d a_or_pk=%s scriptPubKey=%s"
                % (self.my_index, addr_type, self.is_segwit,
                   b2a_hex(addr_or_pubkey), b2a_hex(utxo.scriptPubKey)))

        self.required_key = which_key

        if self.is_segwit:
            if ('pkh' in addr_type):
                # This comment from <https://bitcoincore.org/en/segwit_wallet_dev/>:
                #
                #   Please note that for a P2SH-P2WPKH, the scriptCode is always 26
                #   bytes including the leading size byte, as 0x1976a914{20-byte keyhash}88ac,
                #   NOT the redeemScript nor scriptPubKey
                #
                # Also need this scriptCode for native segwit p2pkh
                #
                assert not self.is_multisig
                self.scriptCode = b'\x19\x76\xa9\x14' + addr + b'\x88\xac'
            elif not self.scriptCode:
                # Segwit P2SH segwit. We need the script!
                if not self.witness_script:
                    raise AssertionError('Need witness script for input #%d' %
                                         self.my_index)

                self.scriptCode = self.get(self.witness_script)
Esempio n. 5
0
    def validate(self, out_idx, txo, my_xfp):
        # do things make sense?
        assert self.my_index == out_idx

        # We might be a change output, because the PSBT
        # creator has given a key path. However, we must be
        # **very** careful and validate this fully.
        # - no output info is needed, in general, so
        #   any output info provided better be right, or fail
        # - full key derivation and validation elsewhere, but critical.
        # - we raise a fraud alarm, since these are not innocent errors
        #
        self.is_change = False

        if not self.subpaths:
            return

        ours = self.parse_subpaths(my_xfp, first_known=True)

        # - must be exactly one of our keys here (extras ignored, not-ours ignored)
        # - not considered fraud because other signers looking at PSBT may have them
        if ours == None:
            return

        expect_pubkey = ours[0]

        # - must match expected address for this output, coming from unsigned txn
        addr_type, addr_or_pubkey, is_segwit = txo.get_address()

        if addr_type == 'p2pk':
            # output is public key (not a hash, much less common)
            assert len(addr_or_pubkey) == 33

            if addr_or_pubkey != expect_pubkey:
                raise FraudulentChangeOutput(
                    "Output#%d: P2PK change output is fraudulent" %
                    self.my_index)

            self.is_change = ours
            return

        expect_pkh = hash160(expect_pubkey)
        pkh = None

        if addr_type == 'p2sh':
            # multisig output
            # we must have the redeem script already (else fail)
            if not self.redeem_script:
                # perhaps an omission, so let's not call fraud on it
                raise AssertionError("Missing redeem script for output #%d" %
                                     self.my_index)

            redeem_script = self.get(self.redeem_script)

            if not is_segwit and \
                    len(redeem_script) == 22 and \
                    redeem_script[0] == 0 and redeem_script[1] == 20:

                # it's actually segwit p2pkh inside p2sh
                pkh = redeem_script[2:22]
            else:
                # multiple keys involved, not supported
                # TODO multisig support
                raise AssertionError(
                    "Not ready for multisig/p2wsh change outputs")

        elif addr_type == 'p2pkh':
            # input is hash160 of a single public key
            assert len(addr_or_pubkey) == 20
            pkh = addr_or_pubkey
        else:
            # we don't know how to "solve" this type of input
            return

        if pkh != expect_pkh:
            raise FraudulentChangeOutput(
                "Output#%d: P2PKH change output is fraudulent" % self.my_index)
        self.is_change = ours
Esempio n. 6
0
    async def doit(self, *a, have_key=None):
        # make the wallet.
        from main import dis

        try:
            from chains import current_chain
            import tcc
            from serializations import hash160
            from stash import blank_object

            if not have_key:
                # get some random bytes
                await ux_dramatic_pause("Picking key...", 2)
                privkey = tcc.secp256k1.generate_secret()
            else:
                # caller must range check this already: 0 < privkey < order
                privkey = have_key

            # calculate corresponding public key value
            pubkey = tcc.secp256k1.publickey(privkey,
                                             True)  # always compressed style

            dis.fullscreen("Rendering...")

            # make payment address
            digest = hash160(pubkey)
            ch = current_chain()
            if self.is_segwit:
                addr = tcc.codecs.bech32_encode(ch.bech32_hrp, 0, digest)
            else:
                addr = tcc.codecs.b58_encode(ch.b58_addr + digest)

            wif = tcc.codecs.b58_encode(ch.b58_privkey + privkey + b'\x01')

            if self.can_do_qr():
                with imported('uqr') as uqr:
                    # make the QR's now, since it's slow
                    is_alnum = self.is_segwit
                    qr_addr = uqr.make(
                        addr if not is_alnum else addr.upper(),
                        min_version=4,
                        max_version=4,
                        encoding=(uqr.Mode_ALPHANUMERIC if is_alnum else 0))

                    qr_wif = uqr.make(wif,
                                      min_version=4,
                                      max_version=4,
                                      encoding=uqr.Mode_BYTE)
            else:
                qr_addr = None
                qr_wif = None

            # Use address as filename. clearly will be unique, but perhaps a bit
            # awkward to work with.
            basename = addr

            dis.fullscreen("Saving...")
            with CardSlot() as card:
                fname, nice_txt = card.pick_filename(
                    basename + ('-note.txt' if self.template_fn else '.txt'))

                with open(fname, 'wt') as fp:
                    self.make_txt(fp, addr, wif, privkey, qr_addr, qr_wif)

                if self.template_fn:
                    fname, nice_pdf = card.pick_filename(basename + '.pdf')

                    with open(fname, 'wb') as fp:
                        self.make_pdf(fp, addr, wif, qr_addr, qr_wif)
                else:
                    nice_pdf = ''

            # Half-hearted attempt to cleanup secrets-contaminated memory
            # - better would be force user to reboot
            # - and yet, we just output the WIF to SDCard anyway
            blank_object(privkey)
            blank_object(wif)
            del qr_wif

        except CardMissingError:
            await needs_microsd()
            return
        except Exception as e:
            await ux_show_story('Failed to write!\n\n\n' + str(e))
            return

        await ux_show_story('Done! Created file(s):\n\n%s\n\n%s' %
                            (nice_txt, nice_pdf))
Esempio n. 7
0
    def sign_tx(self, tx):

        # Create a transaction with all scriptsigs blanekd out
        blank_tx = CTransaction(tx.tx)
        for txin in blank_tx.vin:
            txin.scriptSig = b""

        # Get the master key fingerprint
        master_fp = get_xpub_fingerprint(
            json.loads(self.get_pubkey_at_path('m/0'))['xpub'])

        # create sighashes
        sighash_tuples = []
        for txin, psbt_in, i_num in zip(blank_tx.vin, tx.inputs,
                                        range(len(blank_tx.vin))):
            sighash = b""
            pubkeys = []
            if psbt_in.non_witness_utxo:
                utxo = psbt_in.non_witness_utxo.vout[txin.prevout.n]

                # Check if P2SH
                if utxo.is_p2sh():
                    # Look up redeemscript
                    redeemscript = tx.redeem_scripts[utxo.scriptPubKey[2:22]]
                    # Add to blank_tx
                    txin.scriptSig = redeemscript
                    # Find which pubkeys to sign with for this input
                    for pubkey in tx.hd_keypaths.keys():
                        if pubkey in redeemscript:
                            pubkeys.append(pubkey)
                # Check if P2PKH
                elif utxo.is_p2pkh() or utxo.is_p2pk():
                    txin.scriptSig = psbt_in.non_witness_utxo.vout[
                        txin.prevout.n].scriptPubKey
                    # Find which pubkeys to sign with for this input
                    for pubkey in tx.hd_keypaths.keys():
                        if utxo.is_p2pk and pubkey in utxo.scriptPubKey:
                            pubkeys.append(pubkey)
                        if utxo.is_p2pkh and hash160(
                                pubkey) in utxo.scriptPubKey:
                            pubkeys.append(pubkey)
                # We don't know what this is, skip it
                else:
                    continue

                # Serialize and add sighash ALL
                ser_tx = blank_tx.serialize_without_witness()
                ser_tx += b"\x01\x00\x00\x00"
                print(binascii.hexlify(ser_tx))

                # Hash it
                sighash += hash256(ser_tx)
                print(binascii.hexlify(sighash))
                txin.scriptSig = b""
            elif psbt_in.witness_utxo:
                # Calculate hashPrevouts and hashSequence
                prevouts_preimage = b""
                sequence_preimage = b""
                for inputs in blank_tx.vin:
                    prevouts_preimage += inputs.prevout.serialize()
                    sequence_preimage += struct.pack("<I", inputs.nSequence)
                hashPrevouts = hash256(prevouts_preimage)
                hashSequence = hash256(sequence_preimage)

                # Calculate hashOutputs
                outputs_preimage = b""
                for output in blank_tx.vout:
                    outputs_preimage += output.serialize()
                hashOutputs = hash256(outputs_preimage)

                # Get the scriptCode
                scriptCode = b""
                witness_program = b""
                if psbt_in.witness_utxo.is_p2sh():
                    # Look up redeemscript
                    redeemscript = tx.redeem_scripts[
                        psbt_in.witness_utxo.scriptPubKey[2:22]]
                    witness_program += redeemscript
                else:
                    witness_program += psbt_in.witness_utxo.scriptPubKey

                # Check if witness_program is script hash
                if len(witness_program) == 34 and witness_program[
                        0] == OP_0 and witness_program[1] == 0x20:
                    # look up witnessscript and set as scriptCode
                    witnessscript = tx.witness_scripts[redeemscript[2:]]
                    scriptCode += witnessscript
                else:
                    scriptCode += b"\x19\x76\xa9\x14"
                    scriptCode += redeemscript[2:]
                    scriptCode += b"\x88\xac"

                # Find pubkeys to sign with in the scriptCode

                # Find which pubkeys to sign with for this input
                for pubkey in tx.hd_keypaths.keys():
                    if hash160(pubkey) in scriptCode or pubkey in scriptCode:
                        pubkeys.append(pubkey)

                # Make sighash preimage
                preimage = b""
                preimage += struct.pack("<i", blank_tx.nVersion)
                preimage += hashPrevouts
                preimage += hashSequence
                preimage += txin.prevout.serialize()
                preimage += scriptCode
                preimage += psbt_in.witness_utxo.nValue
                preimage += txin.nSequence
                preimage += hashOutputs
                preimage += struct.pack("<I", tx.tx.nLockTime)
                preimage += b"\x01\x00\x00\x00"

                # hash it
                sighash += hash256(preimage)

            # Figure out which keypath thing is for this input
            for pubkey in pubkeys:
                keypath = tx.hd_keypaths[pubkey]
                print(master_fp)
                print(keypath[0])
                if master_fp == keypath[0]:
                    # Add the keypath strings
                    keypath_str = 'm/'
                    for index in keypath[1:]:
                        keypath_str += str(index) + "/"

                    # Create tuples and add to List
                    tup = (binascii.hexlify(sighash), keypath_str, i_num,
                           pubkey)
                    sighash_tuples.append(tup)

        # Sign the sighashes
        to_send = '{"sign":{"data":['
        for tup in sighash_tuples:
            to_send += '{"hash":"'
            to_send += tup[0]
            to_send += '","keypath":"'
            to_send += tup[1]
            to_send += '"},'
        to_send = to_send[:-1]
        to_send += ']}}'
        print(to_send)

        reply = send_encrypt(to_send, self.password, self.device)
        print(reply)
        if 'error' in reply:
            return
        print(
            "Touch the device for 3 seconds to sign. Touch briefly to cancel")
        reply = send_encrypt(to_send, self.password, self.device)
        print(reply)
        if 'error' in reply:
            return

        # Extract sigs
        sigs = []
        for item in reply['sign']:
            sigs.append(binascii.unhexlify(item['sig']))

        # Make sigs der
        der_sigs = []
        for sig in sigs:
            der_sigs.append(ser_sig_der(sig[0:32], sig[32:64]))

        # add sigs to tx
        for tup, sig in zip(sighash_tuples, der_sigs):
            tx.inputs[tup[2]].partial_sigs[tup[3]] = sig

        # For each input, finalize only p2pkh and p2pk
        for txin, psbt_in in zip(tx.tx.vin, tx.inputs):
            if psbt_in.non_witness_utxo:
                utxo = psbt_in.non_witness_utxo.vout[txin.prevout.n]
                if utxo.is_p2pkh:
                    txin.scriptSig = struct.pack(
                        "B", len(psbt_in.partial_sigs.values()[0])
                    ) + psbt_in.partial_sigs.values()[0] + struct.pack(
                        "B", len(psbt_in.partial_sigs.keys()
                                 [0])) + psbt_in.partial_sigs.keys()[0]
                elif utxo.is_p2pk:
                    txin.scriptSig = truct.pack(
                        "B", len(psbt_in.partial_sigs.values()
                                 [0])) + psbt_in.partial_sigs.values()[0]
                psbt_in.set_null()

        # Extract sigs
        sigs = []
        for item in reply['sign']:
            sigs.append(binascii.unhexlify(item['sig']))

        # Make sigs der
        der_sigs = []
        for sig in sigs:
            der_sigs.append(ser_sig_der(sig[0:32], sig[32:64]))

        # add sigs to tx
        for tup, sig in zip(sighash_tuples, der_sigs):
            tx.inputs[tup[2]].partial_sigs[tup[3]] = sig

        return tx.serialize()