Exemple #1
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 #2
0
def sign_transaction(psbt_in,
                     psbt_out,
                     verbose=False,
                     b64_mode=False,
                     hex_mode=False,
                     finalize=False):
    "Approve a spending transaction by signing it on Coldcard"

    # NOTE: not enforcing policy here on msg contents, so we can define that on product

    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)

    # start the signing process
    ok = dev.send_recv(CCProtocolPacker.sign_transaction(txn_len,
                                                         sha,
                                                         finalize=finalize),
                       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.

    # save it
    if hex_mode:
        result = b2a_hex(result)
    elif b64_mode:
        result = b64encode(result)

    psbt_out.write(result)
Exemple #3
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 #4
0
    def sign_transaction_start(self, raw_psbt, finalize=True):
        # Multiple steps to sign:
        # - upload binary
        # - start signing UX
        # - wait for coldcard to complete process, or have it refused.
        # - download resulting txn
        assert 20 <= len(raw_psbt) < MAX_TXN_LEN, 'PSBT is too big'
        dlen, chk = self.dev.upload_file(raw_psbt)

        resp = self.dev.send_recv(CCProtocolPacker.sign_transaction(dlen, chk, finalize=finalize),
                                    timeout=None)

        if resp != None:
            raise ValueError(resp)
    def sign_transaction_start(self, raw_psbt, finalize=True):
        # Multiple steps to sign:
        # - upload binary
        # - start signing UX
        # - wait for coldcard to complete process, or have it refused.
        # - download resulting txn
        assert 20 <= len(raw_psbt) < MAX_TXN_LEN, 'PSBT is too big'
        dlen, chk = self.dev.upload_file(raw_psbt)

        resp = self.dev.send_recv(CCProtocolPacker.sign_transaction(dlen, chk, finalize=finalize),
                                    timeout=None)

        if resp != None:
            raise ValueError(resp)
Exemple #6
0
    def doit(filename, finalize=False, stxn_flags=0x0):
        if filename[0:5] == b'psbt\xff':
            ip = filename
            filename = 'memory'
        else:
            ip = open(filename, 'rb').read()
            if ip[0:10] == b'70736274ff':
                ip = a2b_hex(ip.strip())
            assert ip[0:5] == b'psbt\xff'

        ll, sha = dev.upload_file(ip)

        dev.send_recv(CCProtocolPacker.sign_transaction(ll, sha, finalize, flags=stxn_flags))

        return ip
Exemple #7
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 #8
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"""
    with get_device() as dev:
        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)

        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 is 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)