Example #1
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_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
Example #3
0
def verify_message_with_address(address: str,
                                sig65: bytes,
                                message: bytes,
                                *,
                                net=None):
    from electroncash.bitcoin import pubkey_to_address
    assert_bytes(sig65, message)
    if net is None: net = constants.net
    try:
        h = Hash(msg_magic(message))
        public_key, compressed = ECPubkey.from_signature65(sig65, h)
        # check public key using the address
        pubkey_hex = public_key.get_public_key_hex(compressed)
        for txin_type in ['p2pkh', 'p2wpkh', 'p2wpkh-p2sh']:
            addr = pubkey_to_address(txin_type, pubkey_hex, net=net)
            if address == addr:
                break
        else:
            raise Exception("Bad signature")
        # check message
        public_key.verify_message_hash(sig65[1:], h)
        return True
    except Exception as e:
        print_error(f"Verification error: {repr(e)}")
        return False
    def parse_message_signature(self, response, message, pubkey):

        # Prepend the message for signing as done inside the card!!
        message = to_bytes(message, 'utf8')
        hash = Hash(msg_magic(message))
        coordx = pubkey.get_public_key_bytes()

        response = bytearray(response)
        recid = -1
        for id in range(4):
            compsig = self.parse_to_compact_sig(response, id, compressed=True)
            # remove header byte
            compsig2 = compsig[1:]

            try:
                pk = ECPubkey.from_sig_string(compsig2, id, hash)
                pkbytes = pk.get_public_key_bytes(compressed=True)
            except InvalidECPointException:
                continue

            if coordx == pkbytes:
                recid = id
                break

        if recid == -1:
            raise ValueError("Unable to recover public key from signature")

        return compsig
Example #5
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
 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
Example #7
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
Example #8
0
 def verify_message_for_address(self, sig65: bytes, message: bytes) -> None:
     assert_bytes(message)
     h = Hash(msg_magic(message))
     public_key, compressed = self.from_signature65(sig65, h)
     # check public key
     if public_key != self:
         raise Exception("Bad signature")
     # check message
     self.verify_message_hash(sig65[1:], h)
Example #9
0
 def checkd_data_sig(self,sig,pre,pk):
     sec, compressed = self.keypair.get(pk)
     pre_hash = Hash(pre)
     pkey = regenerate_key(sec)
     secexp = pkey.secret
     private_key = MySigningKey.from_secret_exponent(secexp, curve=ecdsa.SECP256k1)
     public_key = private_key.get_verifying_key()
     print("Data signature ok:")
     print(public_key.verify_digest(sig[:-1], pre_hash, sigdecode=ecdsa.util.sigdecode_der))
Example #10
0
 def test_001_sufficient_funds(self):
     pubkey_1, pubkey_2, pubkey_3 = self.public_keys[0:3]
     address_1, address_2, address_3 = self.addresses[0:3]
     address_x = "111111111111111111111111111"
     inputs = {
         pubkey_1:[
             fake_hash(address_1, 1000) + ":0",
             fake_hash(address_1, 100)  + ":0"
             ],
         pubkey_2:[
             fake_hash(address_2, 1000) + ":0"]
         }
     self.assertTrue(self.coin.check_inputs_for_sufficient_funds(inputs, 2022))
     self.assertFalse(self.coin.check_inputs_for_sufficient_funds(inputs, 20022))
     bad_hash_input = {Hash("pubkey_1").hex():["a1h4"]}
     self.assertIsNone(self.coin.check_inputs_for_sufficient_funds(bad_hash_input, 2222))
     bad_address_input = {Hash("pubkey_111").hex():["a111h1"]}
     self.assertIsNone(self.coin.check_inputs_for_sufficient_funds(bad_address_input, 2222))
Example #11
0
 def get_transaction_signature(self, tx, sk, vk):
     txin = list(filter(lambda x: vk in x['pubkeys'], tx.inputs()))
     if txin:
         tx_num = tx.inputs().index(txin[0])
         pre_hash = Hash(bfh(tx.serialize_preimage(tx_num)))
         private_key = MySigningKey.from_secret_exponent(sk.secret, curve = SECP256k1)
         public_key = private_key.get_verifying_key()
         sig = private_key.sign_digest_deterministic(pre_hash, hashfunc=hashlib.sha256, sigencode = ecdsa.util.sigencode_der)
         assert public_key.verify_digest(sig, pre_hash, sigdecode = ecdsa.util.sigdecode_der)
         result = bh2u(sig) + int_to_hex(tx.nHashType() & 255, 1)
         return result.encode('utf-8')
     return b''
Example #12
0
 def hid_send_encrypt(self, msg):
     reply = ""
     try:
         secret = Hash(self.password)
         msg = EncodeAES(secret, msg)
         reply = self.hid_send_plain(msg)
         if 'ciphertext' in reply:
             reply = DecodeAES(secret, ''.join(reply["ciphertext"]))
             reply = json.loads(reply)
         if 'error' in reply:
             self.password = None
     except Exception as e:
         print_error('Exception caught ' + str(e))
     return reply
Example #13
0
 def get_transaction_signature(transaction, inputs, secret_keys):
     "get transaction signature"
     signatures = {}
     for txin in transaction.inputs():
         pubkey = txin['pubkeys'][0]
         if pubkey in inputs:
             tx_num = transaction.inputs().index(txin)
             pre_hash = Hash(bfh(transaction.serialize_preimage(tx_num)))
             private_key = MySigningKey.from_secret_exponent(secret_keys[pubkey].secret, curve=SECP256k1)
             public_key = private_key.get_verifying_key()
             sig = private_key.sign_digest_deterministic(pre_hash,
                                                         hashfunc=hashlib.sha256,
                                                         sigencode=ecdsa.util.sigencode_der)
             assert public_key.verify_digest(sig, pre_hash, sigdecode=ecdsa.util.sigdecode_der)
             signatures[txin['tx_hash'] + ":" + str(txin['tx_pos'])] = (bh2u(sig) + int_to_hex(transaction.nHashType() & 255, 1)).encode('utf-8')
     return signatures
Example #14
0
 def verify_tx_signature(signature, transaction, verification_key, utxo):
     '''Verify the signature for a specific utxo ("prevout_hash:n") given a
     transaction and verification key. Ensures that the signature is valid
     AND canonically encoded, so it will be accepted by network.
     '''
     tx_num = None
     for n, x in enumerate(transaction.inputs()):
         if (verification_key in x['pubkeys']
                 and utxo == "{}:{}".format(x['tx_hash'], x['tx_pos'])):
             tx_num = n
             break
     else:
         # verification_key / utxo combo not found in tx inputs, bail
         return False
     # calculate sighash digest (implicitly this is for sighash 0x41)
     pre_hash = Hash(bfh(transaction.serialize_preimage(tx_num)))
     order = generator_secp256k1.order()
     try:
         sigbytes = bfh(signature.decode())
     except ValueError:
         # not properly hex encoded or UnicodeDecodeError (garbage data)
         return False
     if not sigbytes or sigbytes[-1] != 0x41:
         return False
     DERsig = sigbytes[:
                       -1]  # lop off the sighash byte for the DER check below
     try:
         # ensure DER encoding is canonical, and extract r,s if OK
         r, s = CoinUtils.IsValidDERSignatureEncoding_With_Extract(DERsig)
     except AssertionError:
         return False
     if (s << 1) > order:
         # high S values are rejected by BCH network
         return False
     try:
         pubkey_point = ser_to_point(bfh(verification_key))
     except:
         # ser_to_point will fail if pubkey is off-curve, infinity, or garbage.
         return False
     vk = MyVerifyingKey.from_public_point(pubkey_point, curve=SECP256k1)
     try:
         return vk.verify_digest(DERsig,
                                 pre_hash,
                                 sigdecode=ecdsa.util.sigdecode_der)
     except:
         # verify_digest returns True on success, otherwise raises
         return False
Example #15
0
    def sign_message(self, message: bytes, is_compressed: bool) -> bytes:
        def bruteforce_recid(sig_string):
            for recid in range(4):
                sig65 = construct_sig65(sig_string, recid, is_compressed)
                try:
                    self.verify_message_for_address(sig65, message)
                    return sig65, recid
                except Exception as e:
                    continue
            else:
                raise Exception("error: cannot sign message. no recid fits..")

        message = to_bytes(message, 'utf8')
        msg_hash = Hash(msg_magic(message))
        sig_string = self.sign(msg_hash,
                               sigencode=sig_string_from_r_and_s,
                               sigdecode=get_r_and_s_from_sig_string)
        sig65, recid = bruteforce_recid(sig_string)
        return sig65
 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
Example #17
0
    def sign_transaction(self, tx, password):
        self.print_error('sign_transaction(): tx: ' + str(tx))  #debugSatochip

        client = self.get_client()

        # outputs
        txOutputs = ''.join(tx.serialize_output(o) for o in tx.outputs())
        hashOutputs = bh2u(Hash(bfh(txOutputs)))
        txOutputs = var_int(len(tx.outputs())) + txOutputs
        self.print_error('sign_transaction(): hashOutputs= ',
                         hashOutputs)  #debugSatochip
        self.print_error('sign_transaction(): outputs= ',
                         txOutputs)  #debugSatochip

        # Fetch inputs of the transaction to sign
        derivations = self.get_tx_derivations(tx)
        for i, txin in enumerate(tx.inputs()):
            self.print_error('sign_transaction(): input =', i)  #debugSatochip
            self.print_error('sign_transaction(): input[type]:',
                             txin['type'])  #debugSatochip
            if txin['type'] == 'coinbase':
                self.give_error(
                    "Coinbase not supported")  # should never happen

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

            pubkeys, x_pubkeys = tx.get_sorted_pubkeys(txin)
            for j, x_pubkey in enumerate(x_pubkeys):
                self.print_error('sign_transaction(): forforloop: j=',
                                 j)  #debugSatochip
                if tx.is_txin_complete(txin):
                    break

                if x_pubkey in derivations:
                    signingPos = j
                    s = derivations.get(x_pubkey)
                    address_path = "%s/%d/%d" % (self.get_derivation()[2:],
                                                 s[0], s[1])

                    # get corresponing extended key
                    (depth, bytepath) = bip32path2bytes(address_path)
                    (key, chaincode
                     ) = client.cc.card_bip32_get_extendedkey(bytepath)

                    # parse tx
                    pre_tx_hex = tx.serialize_preimage(i)
                    pre_tx = bytes.fromhex(
                        pre_tx_hex)  # hex representation => converted to bytes
                    pre_hash = Hash(bfh(pre_tx_hex))
                    pre_hash_hex = pre_hash.hex()
                    self.print_error('sign_transaction(): pre_tx_hex=',
                                     pre_tx_hex)  #debugSatochip
                    self.print_error('sign_transaction(): pre_hash=',
                                     pre_hash_hex)  #debugSatochip
                    (response, sw1, sw2) = client.cc.card_parse_transaction(
                        pre_tx, True
                    )  # use 'True' since BCH use BIP143 as in Segwit...
                    #print_error('[satochip] sign_transaction(): response= '+str(response)) #debugSatochip
                    (tx_hash, needs_2fa
                     ) = client.parser.parse_parse_transaction(response)
                    # tx_hash should be equal to pre_hash_hex
                    self.print_error('sign_transaction(): tx_hash=',
                                     bytes(tx_hash).hex())  #debugSatochip
                    #todo: assert()

                    # sign tx
                    keynbr = 0xFF  #for extended key
                    if needs_2fa:
                        # format & encrypt msg
                        import json
                        coin_type = 145  #see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
                        test_net = networks.net.TESTNET
                        msg = {
                            'action': "sign_tx",
                            'tx': pre_tx_hex,
                            'ct': coin_type,
                            'sw': True,
                            'tn': test_net,
                            'txo': txOutputs,
                            'ty': txin['type']
                        }
                        msg = json.dumps(msg)
                        (id_2FA,
                         msg_out) = client.cc.card_crypt_transaction_2FA(
                             msg, True)
                        d = {}
                        d['msg_encrypt'] = msg_out
                        d['id_2FA'] = id_2FA
                        # self.print_error("encrypted message: "+msg_out)
                        self.print_error("id_2FA:", id_2FA)

                        #do challenge-response with 2FA device...
                        client.handler.show_message(
                            '2FA request sent! Approve or reject request on your second device.'
                        )
                        run_hook('do_challenge_response', d)
                        # decrypt and parse reply to extract challenge response
                        try:
                            reply_encrypt = None  # init it in case of exc below
                            reply_encrypt = d['reply_encrypt']
                        except Exception as e:
                            # Note: give_error here will raise again.. :/
                            self.give_error(
                                "No response received from 2FA.\nPlease ensure that the Satochip-2FA plugin is enabled in Tools>Optional Features",
                                True)
                            break
                        if reply_encrypt is None:
                            #todo: abort tx
                            break
                        reply_decrypt = client.cc.card_crypt_transaction_2FA(
                            reply_encrypt, False)
                        self.print_error("challenge:response=", reply_decrypt)
                        reply_decrypt = reply_decrypt.split(":")
                        rep_pre_hash_hex = reply_decrypt[0][0:64]
                        if rep_pre_hash_hex != pre_hash_hex:
                            #todo: abort tx or retry?
                            self.print_error("Abort transaction: tx mismatch:",
                                             rep_pre_hash_hex, "!=",
                                             pre_hash_hex)
                            break
                        chalresponse = reply_decrypt[1]
                        if chalresponse == "00" * 20:
                            #todo: abort tx?
                            self.print_error(
                                "Abort transaction: rejected by 2FA!")
                            break
                        chalresponse = list(bytes.fromhex(chalresponse))
                    else:
                        chalresponse = None
                    (tx_sig, sw1, sw2) = client.cc.card_sign_transaction(
                        keynbr, tx_hash, chalresponse)
                    self.print_error('sign_transaction(): sig=',
                                     bytes(tx_sig).hex())  #debugSatochip
                    #todo: check sw1sw2 for error (0x9c0b if wrong challenge-response)
                    # enforce low-S signature (BIP 62)
                    tx_sig = bytearray(tx_sig)
                    r, s = get_r_and_s_from_der_sig(tx_sig)
                    if s > CURVE_ORDER // 2:
                        s = CURVE_ORDER - s
                    tx_sig = der_sig_from_r_and_s(r, s)
                    #update tx with signature
                    tx_sig = tx_sig.hex() + '41'
                    #tx.add_signature_to_txin(i,j,tx_sig)
                    txin['signatures'][j] = tx_sig
                    break
            else:
                self.give_error("No matching x_key for sign_transaction"
                                )  # should never happen

        self.print_error("is_complete", tx.is_complete())
        tx.raw = tx.serialize()
        return
Example #18
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
Example #19
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
Example #20
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()
Example #21
0
 def hash(text):
     ''' Returns sha256(sha256(text)) as bytes. text may be bytes or str. '''
     return Hash(text)  # bitcoin.Hash is sha256(sha256(x))
Example #22
0
def derive_keys(x):
    h = Hash(x)
    h = hashlib.sha512(h).digest()
    return (h[:32],h[32:])
Example #23
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()
Example #24
0
def fake_hash(address, value):
    return Hash("{}{}".format(address, value)).hex()