예제 #1
0
    def __init__(self, H):
        assert isinstance(H, bytes)

        if not seclib:
            try:
                Hpoint = ser_to_point(H)
            except:
                raise ValueError("H could not be parsed")
            HGpoint = Hpoint + ecdsa.SECP256k1.generator
            if HGpoint == ecdsa.ellipticcurve.INFINITY:
                # this happens if H = -G
                raise InsecureHPoint(-1)
            self._ecdsa_H = Hpoint
            self._ecdsa_HG = HGpoint

            self.H = point_to_ser(Hpoint, comp=False)
            self.HG = point_to_ser(HGpoint, comp=False)
        else:
            ctx = seclib.ctx
            H_buf = create_string_buffer(64)
            res = seclib.secp256k1_ec_pubkey_parse(ctx, H_buf, H, c_size_t(len(H)))
            if not res:
                raise ValueError('H could not be parsed by the secp256k1 library')

            self._seclib_H = H_buf.raw

            G = point_to_ser(ecdsa.SECP256k1.generator, comp=False)
            G_buf = create_string_buffer(64)
            res = seclib.secp256k1_ec_pubkey_parse(ctx, G_buf, G, c_size_t(len(G)))
            assert res, "G point should always deserialize without issue"

            HG_buf = create_string_buffer(64)
            publist = (c_void_p*2)(*(cast(x, c_void_p) for x in (H_buf, G_buf)))
            res = seclib.secp256k1_ec_pubkey_combine(ctx, HG_buf, publist, 2)
            if res != 1:
                # this happens if H = -G
                raise InsecureHPoint(-1)

            self._seclib_HG = HG_buf.raw

            # now serialize H and HG as uncompressed bytes
            serpoint = create_string_buffer(65)
            sersize = c_size_t(65)

            res = seclib.secp256k1_ec_pubkey_serialize(ctx, serpoint, byref(sersize), H_buf, secp256k1.SECP256K1_EC_UNCOMPRESSED)
            assert res == 1
            assert sersize.value == 65
            self.H = serpoint.raw

            res = seclib.secp256k1_ec_pubkey_serialize(ctx, serpoint, byref(sersize), HG_buf, secp256k1.SECP256K1_EC_UNCOMPRESSED)
            assert res == 1
            assert sersize.value == 65
            self.HG = serpoint.raw
예제 #2
0
    def __init__(self, master_privkey):
        G = generator_secp256k1
        order = G.order()

        # make two derived private keys (integers between 1 and p-1 inclusive)

        # hard derivation (irreversible):
        x = int.from_bytes(hashlib.sha512(b'Split1' + master_privkey.to_bytes(32, 'big')).digest(), 'big')
        self.priv1 = 1 + (x % (order-1))
        x = int.from_bytes(hashlib.sha512(b'Split2' + master_privkey.to_bytes(32, 'big')).digest(), 'big')
        self.priv2 = 1 + (x % (order-1))

        # soft derivation (reversible):
        #self.priv1 = (0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa * master_privkey) % order
        #self.priv2 = (0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb * master_privkey) % order

        # generate compressed pubkeys
        self.pub1ser = point_to_ser(self.priv1 * G, True)
        self.pub2ser = point_to_ser(self.priv2 * G, True)

        self.keypairs = {
            self.pub1ser.hex() : (self.priv1.to_bytes(32, 'big'), True),
            self.pub2ser.hex() : (self.priv2.to_bytes(32, 'big'), True),
            }

        # params from ABC 0.18.2 unit tests
        cds_sig = bytes.fromhex('30440220256c12175e809381f97637933ed6ab97737d263eaaebca6add21bced67fd12a402205ce29ecc1369d6fc1b51977ed38faaf41119e3be1d7edfafd7cfaf0b6061bd07')
        cds_msg = b''
        cds_pubkey = bytes.fromhex('038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508')
        self.redeemscript = joinbytes([
            OpCodes.OP_IF,
                #this branch can only run on CDS-supporting chain
                len(cds_sig), cds_sig,
                len(cds_msg), cds_msg,
                len(cds_pubkey), cds_pubkey,
                OpCodes.OP_CHECKDATASIGVERIFY,
                len(self.pub1ser), self.pub1ser,
            OpCodes.OP_ELSE,
                #this branch can run on any chain
                len(self.pub2ser), self.pub2ser,
            OpCodes.OP_ENDIF,
            OpCodes.OP_CHECKSIG
            ])
        assert 76 < len(self.redeemscript) <= 255  # simplify push in scriptsig; note len is around 200.

        self.address = Address.from_multisig_script(self.redeemscript)

        # make dummy scripts of correct size for size estimation.
        self.dummy_scriptsig_redeem = '01'*(4 + 72 + len(self.redeemscript))
        self.dummy_scriptsig_refund = '00'*(4 + 72 + len(self.redeemscript))
 def verify_tx_signature(self, signature, transaction, verification_key):
     "It verifies the transaction signatures"
     txin = list(
         filter(lambda x: verification_key in x['pubkeys'],
                transaction.inputs()))
     if txin:
         tx_num = transaction.inputs().index(txin[0])
         pre_hash = Hash(bfh(transaction.serialize_preimage(tx_num)))
         order = generator_secp256k1.order()
         r, s = ecdsa.util.sigdecode_der(bfh(signature.decode()[:-2]),
                                         order)
         sig_string = ecdsa.util.sigencode_string(r, s, order)
         compressed = len(verification_key) <= 66
         for recid in range(0, 4):
             try:
                 pubk = MyVerifyingKey.from_signature(sig_string,
                                                      recid,
                                                      pre_hash,
                                                      curve=SECP256k1)
                 pubkey = bh2u(point_to_ser(pubk.pubkey.point, compressed))
                 if verification_key == pubkey:
                     return True
             except:
                 continue
     else:
         return False
예제 #4
0
 def verify_tx_signature(self, signature, transaction, verification_key,
                         utxo):
     '''Verify the signature for a specific utxo ("prevout_hash:n") given a
     transaction and verification key.'''
     txin = list(
         filter(
             lambda x:
             (verification_key in x['pubkeys'] and utxo == "{}:{}".format(
                 x['tx_hash'], x['tx_pos'])), transaction.inputs()))
     if txin:
         tx_num = transaction.inputs().index(txin[0])
         pre_hash = Hash(bfh(transaction.serialize_preimage(tx_num)))
         order = generator_secp256k1.order()
         r, s = ecdsa.util.sigdecode_der(bfh(signature.decode()[:-2]),
                                         order)
         sig_string = ecdsa.util.sigencode_string(r, s, order)
         compressed = len(verification_key) <= 66
         for recid in range(0, 4):
             try:
                 pubk = MyVerifyingKey.from_signature(sig_string,
                                                      recid,
                                                      pre_hash,
                                                      curve=SECP256k1)
                 pubkey = bh2u(point_to_ser(pubk.pubkey.point, compressed))
                 if verification_key == pubkey:
                     return True
             except:
                 continue
     else:
         return False
예제 #5
0
    def sign_message(self, sequence, message, password):
        sig = None
        try:
            inputPath = self.get_derivation() + "/%d/%d" % sequence
            inputHash = Hash(msg_magic(message)).encode('hex')
            hasharray = []
            hasharray.append({'hash': inputHash, 'keypath': inputPath})
            hasharray = json.dumps(hasharray)

            msg = '{"sign":{"meta":"sign message", "data":%s}}' % (hasharray)

            dbb_client = self.plugin.get_client(self)

            if not dbb_client.is_paired():
                raise Exception("Could not sign message.")

            reply = dbb_client.hid_send_encrypt(msg)
            self.handler.show_message(_("Signing message ...\r\n\r\n" \
                                        "To continue, touch the Digital Bitbox's blinking light for 3 seconds.\r\n\r\n" \
                                        "To cancel, briefly touch the blinking light or wait for the timeout."))
            reply = dbb_client.hid_send_encrypt(
                msg
            )  # Send twice, first returns an echo for smart verification (not implemented)
            self.handler.clear_dialog()

            if 'error' in reply:
                raise Exception(reply['error']['message'])

            if 'sign' not in reply:
                raise Exception("Could not sign message.")

            if 'recid' in reply['sign'][0]:
                # firmware > v2.1.1
                sig = chr(27 + int(reply['sign'][0]['recid'], 16) +
                          4) + reply['sign'][0]['sig'].decode('hex')
                h = Hash(msg_magic(message))
                pk, compressed = pubkey_from_signature(sig, h)
                pk = point_to_ser(pk.pubkey.point, compressed)
                addr = public_key_to_p2pkh(pk)
                if verify_message(addr, sig, message) is False:
                    raise Exception("Could not sign message")
            elif 'pubkey' in reply['sign'][0]:
                # firmware <= v2.1.1
                for i in range(4):
                    sig = chr(27 + i +
                              4) + reply['sign'][0]['sig'].decode('hex')
                    try:
                        addr = public_key_to_p2pkh(
                            reply['sign'][0]['pubkey'].decode('hex'))
                        if verify_message(addr, sig, message):
                            break
                    except Exception:
                        continue
                else:
                    raise Exception("Could not sign message")

        except BaseException as e:
            self.give_error(e)
        return sig
 def verify_signature(self, signature, message, verification_key):
     "This method verifies signature of message"
     pk, compressed = pubkey_from_signature(signature,
                                            Hash(msg_magic(message)))
     address_from_signature = public_key_to_p2pkh(
         point_to_ser(pk.pubkey.point, compressed))
     address_from_vk = self.address(verification_key)
     return address_from_signature == address_from_vk
예제 #7
0
    def _calc_initial(self):
        Hpoint = self.setup._ecdsa_H
        HGpoint = self.setup._ecdsa_HG

        k = self.nonce
        a = self.amount_mod

        # We don't want to calculate (a * Hpoint) since the time to execute
        # would reveal information about size / bitcount of a. So, we use
        # the nonce as a blinding offset factor.
        Ppoint = ((a - k) % order) * Hpoint +  k * HGpoint

        if Ppoint == ecdsa.ellipticcurve.INFINITY:
            raise ResultAtInfinity

        self.P_uncompressed = point_to_ser(Ppoint, comp=False)
        self.P_compressed = point_to_ser(Ppoint, comp=True)
예제 #8
0
 def verify_signature(self, signature, message, verification_key):
     "This method verifies signature of message"
     try:
         pk, compressed = pubkey_from_signature(signature, Hash(msg_magic(message)))
         pubkey = point_to_ser(pk.pubkey.point, compressed).hex()
         return pubkey == verification_key
     except Exception as e:
         self.print_error("verify_signature:", repr(e))
         return False
예제 #9
0
def add_points(points_iterable):
    """ Adds one or more serialized points together. This is fastest if the
    points are already uncompressed. Returns uncompressed point.

    Note: intermediate sums are allowed to be the point at infinity, but not the
    final result.
    """
    plist = []
    if seclib:
        ctx = seclib.ctx
        for pser in points_iterable:
            P_buf = create_string_buffer(64)
            _b = bytes(pser)
            res = seclib.secp256k1_ec_pubkey_parse(ctx, P_buf, _b, c_size_t(len(_b)))
            if not res:
                raise ValueError('point could not be parsed by the secp256k1 library')
            plist.append(P_buf)
        if not plist:
            raise ValueError('empty list')

        num = len(plist)
        result_buf = create_string_buffer(64)
        publist = (c_void_p*num)(*(cast(x, c_void_p) for x in plist))
        res = seclib.secp256k1_ec_pubkey_combine(ctx, result_buf, publist, num)
        if res != 1:
            raise ResultAtInfinity

        serpoint = create_string_buffer(65)
        sersize = c_size_t(65)
        res = seclib.secp256k1_ec_pubkey_serialize(ctx, serpoint, byref(sersize), result_buf, secp256k1.SECP256K1_EC_UNCOMPRESSED)
        assert res == 1
        assert sersize.value == 65
        return serpoint.raw
    else:
        for pser in points_iterable:
            plist.append(ser_to_point(pser))
        if not plist:
            raise ValueError('empty list')
        Psum = sum(plist[1:], plist[0])
        if Psum == ecdsa.ellipticcurve.INFINITY:
            raise ResultAtInfinity
        return point_to_ser(Psum, comp=False)
예제 #10
0
    def __init__(self, master_privkey):
        G = generator_secp256k1
        order = G.order()

        # make two derived private keys (integers between 1 and p-1 inclusive)

        # hard derivation (irreversible):
        x = int.from_bytes(
            hashlib.sha512(b'SplitMUL' +
                           master_privkey.to_bytes(32, 'big')).digest(), 'big')
        self.priv1 = 1 + (x % (order - 1))

        # generate compressed pubkeys
        self.pub1ser = point_to_ser(self.priv1 * G, True)

        self.keypairs = {
            self.pub1ser.hex(): (self.priv1.to_bytes(32, 'big'), True),
        }

        self.redeemscript = joinbytes([
            #this script can only run on MUL-supporting chain
            len(self.pub1ser),
            self.pub1ser,
            OpCodes.OP_CHECKSIGVERIFY,
            OpCodes.OP_1,
            OpCodes.OP_1,
            OpCodes.OP_MUL,
        ])
        assert len(
            self.redeemscript
        ) <= 76  # simplify push in scriptsig; note len is around 38.

        self.address = Address.from_multisig_script(self.redeemscript)

        # make dummy scripts of correct size for size estimation.
        self.dummy_scriptsig_redeem = '01' * (3 + 72 + len(self.redeemscript))
 def restore_from_privkey(self, secret_string):
     self.private_key = string_to_number(bytes.fromhex(secret_string))
     self.eck = EC_KEY(bytes.fromhex(secret_string))
     self.public_key = point_to_ser(self.private_key * self.G, True)
예제 #12
0
 def verify_signature(self, sig, message, vk):
     pk, compressed = pubkey_from_signature(sig,Hash(msg_magic(message)))
     address_from_signature = public_key_to_p2pkh(point_to_ser(pk.pubkey.point,compressed))
     address_from_vk = self.address(vk)
     return address_from_signature == address_from_signature
예제 #13
0
    def sign_transaction(self, tx, password):
        if tx.is_complete():
            return

        try:
            p2shTransaction = False
            derivations = self.get_tx_derivations(tx)
            inputhasharray = []
            hasharray = []
            pubkeyarray = []

            # Build hasharray from inputs
            for i, txin in enumerate(tx.inputs()):
                if txin['type'] == 'coinbase':
                    self.give_error(
                        "Coinbase not supported")  # should never happen

                if txin['type'] in ['p2sh']:
                    p2shTransaction = True

                for x_pubkey in txin['x_pubkeys']:
                    if x_pubkey in derivations:
                        index = derivations.get(x_pubkey)
                        inputPath = "%s/%d/%d" % (self.get_derivation(),
                                                  index[0], index[1])
                        inputHash = Hash(
                            tx.serialize_preimage(i).decode('hex'))
                        hasharray_i = {
                            'hash': inputHash.encode('hex'),
                            'keypath': inputPath
                        }
                        hasharray.append(hasharray_i)
                        inputhasharray.append(inputHash)
                        break
                else:
                    self.give_error("No matching x_key for sign_transaction"
                                    )  # should never happen

            # Sanity check
            if p2shTransaction:
                for txinput in tx.inputs():
                    if txinput['type'] != 'p2sh':
                        self.give_error(
                            "P2SH / regular input mixed in same transaction not supported"
                        )  # should never happen

            # Build pubkeyarray from outputs (unused because echo for smart verification not implemented)
            if not p2shTransaction:
                for _type, address, amount in tx.outputs():
                    assert _type == TYPE_ADDRESS
                    info = tx.output_info.get(address)
                    if info is not None:
                        index, xpubs, m = info
                        changePath = self.get_derivation() + "/%d/%d" % index
                        changePubkey = self.derive_pubkey(index[0], index[1])
                        pubkeyarray_i = {
                            'pubkey': changePubkey,
                            'keypath': changePath
                        }
                        pubkeyarray.append(pubkeyarray_i)

            # Build sign command
            dbb_signatures = []
            steps = math.ceil(1.0 * len(hasharray) / self.maxInputs)
            for step in range(int(steps)):
                hashes = hasharray[step * self.maxInputs:(step + 1) *
                                   self.maxInputs]

                msg = '{"sign": {"meta":"%s", "data":%s, "checkpub":%s} }' % \
                       (Hash(tx.serialize()).encode('hex'), json.dumps(hashes), json.dumps(pubkeyarray))

                dbb_client = self.plugin.get_client(self)

                if not dbb_client.is_paired():
                    raise Exception("Could not sign transaction.")

                reply = dbb_client.hid_send_encrypt(msg)

                if 'error' in reply:
                    raise Exception(reply['error']['message'])

                if 'echo' not in reply:
                    raise Exception("Could not sign transaction.")

                if steps > 1:
                    self.handler.show_message(_("Signing large transaction. Please be patient ...\r\n\r\n" \
                                                "To continue, touch the Digital Bitbox's blinking light for 3 seconds. " \
                                                "(Touch " + str(step + 1) + " of " + str(int(steps)) + ")\r\n\r\n" \
                                                "To cancel, briefly touch the blinking light or wait for the timeout.\r\n\r\n"))
                else:
                    self.handler.show_message(_("Signing transaction ...\r\n\r\n" \
                                                "To continue, touch the Digital Bitbox's blinking light for 3 seconds.\r\n\r\n" \
                                                "To cancel, briefly touch the blinking light or wait for the timeout."))

                reply = dbb_client.hid_send_encrypt(
                    msg
                )  # Send twice, first returns an echo for smart verification (not implemented)
                self.handler.clear_dialog()

                if 'error' in reply:
                    raise Exception(reply['error']['message'])

                if 'sign' not in reply:
                    raise Exception("Could not sign transaction.")

                dbb_signatures.extend(reply['sign'])

            # Fill signatures
            if len(dbb_signatures) <> len(tx.inputs()):
                raise Exception("Incorrect number of transactions signed."
                                )  # Should never occur
            for i, txin in enumerate(tx.inputs()):
                num = txin['num_sig']
                for pubkey in txin['pubkeys']:
                    signatures = filter(None, txin['signatures'])
                    if len(signatures) == num:
                        break  # txin is complete
                    ii = txin['pubkeys'].index(pubkey)
                    signed = dbb_signatures[i]
                    if 'recid' in signed:
                        # firmware > v2.1.1
                        recid = int(signed['recid'], 16)
                        s = signed['sig'].decode('hex')
                        h = inputhasharray[i]
                        pk = MyVerifyingKey.from_signature(s,
                                                           recid,
                                                           h,
                                                           curve=SECP256k1)
                        pk = point_to_ser(pk.pubkey.point, True).encode('hex')
                    elif 'pubkey' in signed:
                        # firmware <= v2.1.1
                        pk = signed['pubkey']
                    if pk != pubkey:
                        continue
                    sig_r = int(signed['sig'][:64], 16)
                    sig_s = int(signed['sig'][64:], 16)
                    sig = sigencode_der(sig_r, sig_s,
                                        generator_secp256k1.order())
                    txin['signatures'][ii] = sig.encode('hex') + int_to_hex(
                        Transaction.nHashType() & 255, 1)
                    tx._inputs[i] = txin

        except BaseException as e:
            self.give_error(e, True)
        else:
            print_error("Transaction is_complete", tx.is_complete())
            tx.raw = tx.serialize()
 def verify_signature(self, signature, message, verification_key):
     "This method verifies signature of message"
     pk, compressed = pubkey_from_signature(signature,
                                            Hash(msg_magic(message)))
     pubkey = point_to_ser(pk.pubkey.point, compressed).hex()
     return pubkey == verification_key
 def generate_key_pair(self):
     self.private_key = ecdsa.util.randrange(pow(2, 256)) % self._r
     self.eck = EC_KEY(number_to_string(self.private_key, self._r))
     self.public_key = point_to_ser(self.private_key * self.G, True)
예제 #16
0
 def generate_key_pair(self):
     """ generate encryption/decryption pair """
     self.private_key = ecdsa.util.randrange(self._r)
     self.eck = EC_KEY(number_to_string(self.private_key, self._r))
     self.public_key = point_to_ser(self.private_key * self.G, True)
예제 #17
0
    def sign_message(self,
                     sequence,
                     message,
                     password,
                     sigtype=SignatureType.BITCOIN):
        if sigtype == SignatureType.ECASH:
            raise RuntimeError(
                _('eCash message signing is not available for {}').format(
                    self.device))
        sig = None
        try:
            message = message.encode('utf8')
            inputPath = self.get_derivation() + "/%d/%d" % sequence
            msg_hash = Hash(msg_magic(message))
            inputHash = to_hexstr(msg_hash)
            hasharray = []
            hasharray.append({'hash': inputHash, 'keypath': inputPath})
            hasharray = json.dumps(hasharray)

            msg = b'{"sign":{"meta":"sign message", "data":%s}}' % hasharray.encode(
                'utf8')

            dbb_client = self.plugin.get_client(self)

            if not dbb_client.is_paired():
                raise Exception(_("Could not sign message."))

            reply = dbb_client.hid_send_encrypt(msg)
            self.handler.show_message(
                _("Signing message ...") + "\n\n" +
                _("To continue, touch the Digital Bitbox's blinking light for 3 seconds."
                  ) + "\n\n" +
                _("To cancel, briefly touch the blinking light or wait for the timeout."
                  ))
            reply = dbb_client.hid_send_encrypt(
                msg
            )  # Send twice, first returns an echo for smart verification (not implemented)
            self.handler.finished()

            if 'error' in reply:
                raise Exception(reply['error']['message'])

            if 'sign' not in reply:
                raise Exception(_("Could not sign message."))

            if 'recid' in reply['sign'][0]:
                # firmware > v2.1.1
                sig = bytes([27 + int(reply['sign'][0]['recid'], 16) + 4
                             ]) + binascii.unhexlify(reply['sign'][0]['sig'])
                pk, compressed = pubkey_from_signature(sig, msg_hash)
                pk = point_to_ser(pk.pubkey.point, compressed)
                addr = public_key_to_p2pkh(pk)
                if verify_message(addr, sig, message) is False:
                    raise Exception(_("Could not sign message"))
            elif 'pubkey' in reply['sign'][0]:
                # firmware <= v2.1.1
                for i in range(4):
                    sig = bytes([27 + i + 4]) + binascii.unhexlify(
                        reply['sign'][0]['sig'])
                    try:
                        addr = public_key_to_p2pkh(
                            binascii.unhexlify(reply['sign'][0]['pubkey']))
                        if verify_message(addr, sig, message):
                            break
                    except Exception:
                        continue
                else:
                    raise Exception(_("Could not sign message"))

        except BaseException as e:
            self.give_error(e)
        return sig
예제 #18
0
 def restore_from_privkey(self, secret_string):
     "restore key pair from private key expressed in a hex form"
     self.private_key = string_to_number(bytes.fromhex(secret_string))
     self.eck = EC_KEY(bytes.fromhex(secret_string))
     self.public_key = point_to_ser(self.private_key * self.G, True)
예제 #19
0
    def sign_transaction(self, tx, password):
        if tx.is_complete():
            return

        try:
            p2pkhTransaction = True
            derivations = self.get_tx_derivations(tx)
            inputhasharray = []
            hasharray = []
            pubkeyarray = []

            # Build hasharray from inputs
            for i, txin in enumerate(tx.inputs()):
                if txin['type'] == 'coinbase':
                    self.give_error("Coinbase not supported") # should never happen

                if txin['type'] != 'p2pkh':
                    p2pkhTransaction = False

                for x_pubkey in txin['x_pubkeys']:
                    if x_pubkey in derivations:
                        index = derivations.get(x_pubkey)
                        inputPath = "%s/%d/%d" % (self.get_derivation(), index[0], index[1])
                        inputHash = Hash(binascii.unhexlify(tx.serialize_preimage(i)))
                        hasharray_i = {'hash': to_hexstr(inputHash), 'keypath': inputPath}
                        hasharray.append(hasharray_i)
                        inputhasharray.append(inputHash)
                        break
                else:
                    self.give_error("No matching x_key for sign_transaction") # should never happen

            # Build pubkeyarray from outputs
            for _type, address, amount in tx.outputs():
                if not _type == TYPE_ADDRESS:
                    self.give_error(_("Only address outputs are supported by {}").format(self.name))
                info = tx.output_info.get(address)
                if info is not None:
                    index, xpubs, m, script_type = info
                    changePath = self.get_derivation() + "/%d/%d" % index
                    changePubkey = self.derive_pubkey(index[0], index[1])
                    pubkeyarray_i = {'pubkey': changePubkey, 'keypath': changePath}
                    pubkeyarray.append(pubkeyarray_i)

            # Special serialization of the unsigned transaction for
            # the mobile verification app.
            # At the moment, verification only works for p2pkh transactions.
            if p2pkhTransaction:
                class CustomTXSerialization(Transaction):
                    @classmethod
                    def input_script(self, txin, estimate_size=False, sign_schnorr=False):
                        if txin['type'] == 'p2pkh':
                            return Transaction.get_preimage_script(txin)
                        if txin['type'] == 'p2sh':
                            # Multisig verification has partial support, but is disabled. This is the
                            # expected serialization though, so we leave it here until we activate it.
                            return '00' + push_script(Transaction.get_preimage_script(txin))
                        raise Exception("unsupported type %s" % txin['type'])
                tx_dbb_serialized = CustomTXSerialization(tx.serialize()).serialize()
            else:
                # We only need this for the signing echo / verification.
                tx_dbb_serialized = None

            # Build sign command
            dbb_signatures = []
            steps = math.ceil(1.0 * len(hasharray) / self.maxInputs)
            for step in range(int(steps)):
                hashes = hasharray[step * self.maxInputs : (step + 1) * self.maxInputs]

                msg = {
                    "sign": {
                        "data": hashes,
                        "checkpub": pubkeyarray,
                    },
                }
                if tx_dbb_serialized is not None:
                    msg["sign"]["meta"] = to_hexstr(Hash(tx_dbb_serialized))
                msg = json.dumps(msg).encode('ascii')
                dbb_client = self.plugin.get_client(self)

                if not dbb_client.is_paired():
                    raise Exception("Could not sign transaction.")

                reply = dbb_client.hid_send_encrypt(msg)
                if 'error' in reply:
                    raise Exception(reply['error']['message'])

                if 'echo' not in reply:
                    raise Exception("Could not sign transaction.")

                if self.plugin.is_mobile_paired() and tx_dbb_serialized is not None:
                    reply['tx'] = tx_dbb_serialized
                    self.plugin.comserver_post_notification(reply)

                if steps > 1:
                    self.handler.show_message(_("Signing large transaction. Please be patient ...") + "\n\n" +
                                              _("To continue, touch the Digital Bitbox's blinking light for 3 seconds.") + " " +
                                              _("(Touch {} of {})").format((step + 1), steps) + "\n\n" +
                                              _("To cancel, briefly touch the blinking light or wait for the timeout.") + "\n\n")
                else:
                    self.handler.show_message(_("Signing transaction...") + "\n\n" +
                                              _("To continue, touch the Digital Bitbox's blinking light for 3 seconds.") + "\n\n" +
                                              _("To cancel, briefly touch the blinking light or wait for the timeout."))

                # Send twice, first returns an echo for smart verification
                reply = dbb_client.hid_send_encrypt(msg)
                self.handler.finished()

                if 'error' in reply:
                    if reply["error"].get('code') in (600, 601):
                        # aborted via LED short touch or timeout
                        raise UserCancelled()
                    raise Exception(reply['error']['message'])

                if 'sign' not in reply:
                    raise Exception("Could not sign transaction.")

                dbb_signatures.extend(reply['sign'])

            # Fill signatures
            if len(dbb_signatures) != len(tx.inputs()):
                raise Exception("Incorrect number of transactions signed.") # Should never occur
            for i, txin in enumerate(tx.inputs()):
                num = txin['num_sig']
                for pubkey in txin['pubkeys']:
                    signatures = list(filter(None, txin['signatures']))
                    if len(signatures) == num:
                        break # txin is complete
                    ii = txin['pubkeys'].index(pubkey)
                    signed = dbb_signatures[i]
                    if 'recid' in signed:
                        # firmware > v2.1.1
                        recid = int(signed['recid'], 16)
                        s = binascii.unhexlify(signed['sig'])
                        h = inputhasharray[i]
                        pk = MyVerifyingKey.from_signature(s, recid, h, curve = SECP256k1)
                        pk = to_hexstr(point_to_ser(pk.pubkey.point, True))
                    elif 'pubkey' in signed:
                        # firmware <= v2.1.1
                        pk = signed['pubkey']
                    if pk != pubkey:
                        continue
                    sig_r = int(signed['sig'][:64], 16)
                    sig_s = int(signed['sig'][64:], 16)
                    sig = sigencode_der(sig_r, sig_s, generator_secp256k1.order())
                    txin['signatures'][ii] = to_hexstr(sig) + '41'
                    tx._inputs[i] = txin
        except UserCancelled:
            raise
        except BaseException as e:
            self.give_error(e, True)
        else:
            print_error("Transaction is_complete", tx.is_complete())
            tx.raw = tx.serialize()
예제 #20
0
 def generate_fake_key_pair(self):
     self.fake_private_key = ecdsa.util.randrange(self._r)
     self.fake_eck = EC_KEY(number_to_string(self.fake_private_key,
                                             self._r))
     self.fake_public_key = point_to_ser(self.fake_private_key * self.G,
                                         True)