Exemple #1
0
    def doit(accept=True,
             in_psbt=None,
             finalize=False,
             accept_ms_import=False,
             expect_txn=True):

        if accept_ms_import:
            # XXX would be better to do cap_story here, but that would limit test to simulator
            need_keypress('y')
            time.sleep(0.050)

        if accept != None:
            need_keypress('y' if accept else 'x', timeout=None)

        if accept == False:
            with pytest.raises(CCUserRefused):
                done = None
                while done == None:
                    time.sleep(0.050)
                    done = dev.send_recv(CCProtocolPacker.get_signed_txn(),
                                         timeout=None)
            return
        else:
            done = None
            while done == None:
                time.sleep(0.00)
                done = dev.send_recv(CCProtocolPacker.get_signed_txn(),
                                     timeout=None)

        assert len(done) == 2

        resp_len, chk = done
        psbt_out = dev.download_file(resp_len, chk)

        if not expect_txn:
            # skip checks; it's text
            return psbt_out

        if not finalize:
            if in_psbt:
                from psbt import BasicPSBT
                assert BasicPSBT().parse(in_psbt) != None
        else:
            from pycoin.tx.Tx import Tx
            # parse it
            res = psbt_out
            assert res[0:4] != b'psbt', 'still a PSBT, but asked for finalize'
            t = Tx.from_bin(res)
            assert t.version in [1, 2]

        return psbt_out
Exemple #2
0
def sign_transaction(psbt_in, psbt_out=None, verbose=False, b64_mode=False, hex_mode=False, finalize=False, visualize=False, signed=False):
    "Approve a spending transaction by signing it on Coldcard"

    dev = ColdcardDevice(sn=force_serial)
    dev.check_mitm()

    # Handle non-binary encodings, and incorrect files.
    taste = psbt_in.read(10)
    psbt_in.seek(0)
    if taste == b'70736274ff' or taste == b'70736274FF':
        # Looks hex encoded; make into binary again
        hx = ''.join(re.findall(r'[0-9a-fA-F]*', psbt_in.read().decode('ascii')))
        psbt_in = io.BytesIO(a2b_hex(hx))
    elif taste[0:6] == b'cHNidP':
        # Base64 encoded input
        psbt_in = io.BytesIO(b64decode(psbt_in.read()))
    elif taste[0:5] != b'psbt\xff':
        click.echo("File doesn't have PSBT magic number at start.")
        sys.exit(1)

    # upload the transaction
    txn_len, sha = real_file_upload(psbt_in, dev=dev)

    flags = 0x0
    if visualize or signed:
        flags |= STXN_VISUALIZE
        if signed:
            flags |= STXN_SIGNED
    elif finalize:
        flags |= STXN_FINALIZE

    # start the signing process
    ok = dev.send_recv(CCProtocolPacker.sign_transaction(txn_len, sha, flags=flags), timeout=None)
    assert ok == None

    # errors will raise here, no need for error display
    result, _ = wait_and_download(dev, CCProtocolPacker.get_signed_txn(), 1)

    # If 'finalize' is set, we are outputing a bitcoin transaction,
    # ready for the p2p network. If the CC wasn't able to finalize it,
    # an exception would have occured. Most people will want hex here, but
    # resisting the urge to force it.

    if visualize:
        if psbt_out:
            psbt_out.write(result)
        else:
            click.echo(result, nl=False)
    else:
        # save it
        if hex_mode:
            result = b2a_hex(result)
        elif b64_mode or (not psbt_out and os.isatty(0)):
            result = b64encode(result)

        if psbt_out:
            psbt_out.write(result)
        else:
            click.echo(result)
Exemple #3
0
    def sign_tx(self, tx):
        self.device.check_mitm()

        # Get psbt in hex and then make binary
        fd = io.BytesIO(base64.b64decode(tx.serialize()))

        # learn size (portable way)
        offset = 0
        sz = fd.seek(0, 2)
        fd.seek(0)

        left = sz
        chk = sha256()
        for pos in range(0, sz, MAX_BLK_LEN):
            here = fd.read(min(MAX_BLK_LEN, left))
            if not here: break
            left -= len(here)
            result = self.device.send_recv(
                CCProtocolPacker.upload(pos, sz, here))
            assert result == pos
            chk.update(here)

        # do a verify
        expect = chk.digest()
        result = self.device.send_recv(CCProtocolPacker.sha256())
        assert len(result) == 32
        if result != expect:
            raise ValueError("Wrong checksum:\nexpect: %s\n   got: %s" %
                             (b2a_hex(expect).decode('ascii'),
                              b2a_hex(result).decode('ascii')))

        # start the signing process
        ok = self.device.send_recv(CCProtocolPacker.sign_transaction(
            sz, expect),
                                   timeout=None)
        assert ok == None
        if self.device.is_simulator:
            self.device.send_recv(CCProtocolPacker.sim_keypress(b'y'))

        print("Waiting for OK on the Coldcard...")

        while 1:
            time.sleep(0.250)
            done = self.device.send_recv(CCProtocolPacker.get_signed_txn(),
                                         timeout=None)
            if done == None:
                continue
            break

        if len(done) != 2:
            raise ValueError('Failed: %r' % done)

        result_len, result_sha = done

        result = self.device.download_file(result_len,
                                           result_sha,
                                           file_number=1)
        return {'psbt': base64.b64encode(result).decode()}
Exemple #4
0
    async def sign_psbt(self, data, finalize=False, flags=0x0):
        # upload it first

        async with self.sign_lock:
            sz, chk = self.dev.upload_file(data)
            assert chk == a2b_hex(STATUS.psbt_hash)

            await self.send_recv(
                CCProtocolPacker.sign_transaction(sz, chk, finalize, flags))

            # wait for it to finish
            return await self.wait_and_download(
                CCProtocolPacker.get_signed_txn())
Exemple #5
0
def sign_transaction(psbt_in,
                     psbt_out,
                     verbose=False,
                     hex_mode=False,
                     finalize=True):
    "Approve a spending transaction (by signing it on Coldcard)"

    dev = ColdcardDevice(sn=force_serial)

    dev.check_mitm()

    # not enforcing policy here on msg contents, so we can define that on product
    taste = psbt_in.read(10)
    psbt_in.seek(0)
    if taste == b'70736274ff':
        # hex encoded; make binary
        psbt_in = io.BytesIO(a2b_hex(psbt_in.read()))
        hex_mode = True
    elif taste[0:5] != b'psbt\xff':
        click.echo("File doesn't have PSBT magic number at start.")
        sys.exit(1)

    # upload the transaction
    txn_len, sha = real_file_upload(psbt_in, dev=dev)

    # start the signing process
    ok = dev.send_recv(CCProtocolPacker.sign_transaction(txn_len, sha),
                       timeout=None)
    assert ok == None

    result, _ = wait_and_download(dev, CCProtocolPacker.get_signed_txn(), 1)

    if finalize:
        # assume(?) transaction is completely signed, and output the
        # bitcoin transaction to be sent.
        # XXX maybe do this on embedded side, when txn is final?
        # XXX otherwise, need to parse PSBT and also handle combining properly
        pass

    # save it
    psbt_out.write(b2a_hex(result) if hex_mode else result)
Exemple #6
0
 def sign_transaction_poll(self):
     # poll device... if user has approved, will get tuple: (legnth, checksum) else None
     return self.dev.send_recv(CCProtocolPacker.get_signed_txn(),
                               timeout=None)
Exemple #7
0
 def sign_transaction_poll(self):
     # poll device... if user has approved, will get tuple: (legnth, checksum) else None
     return self.dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None)