Example #1
0
def verify_digest(hash_hex, pubkey_hex, sigb64, hashfunc=hashlib.sha256):
    """
    Given a digest, public key (as hex), and a base64 signature,
    verify that the public key signed the digest.
    Return True if so
    Return False if not
    """
    # NOTE: this method uses the ecdsa package, not cryptography.
    # it is much slower, since it's pure Python.

    assert isinstance(hash_hex, (str, unicode))
    hash_hex = str(hash_hex)

    sig_r, sig_s = decode_signature(sigb64)
    pubk_uncompressed_hex = keylib.key_formatting.decompress(pubkey_hex)

    sig_bin = sigencode_der( sig_r, sig_s, SECP256k1.order )
    vk = VerifyingKey.from_string(pubk_uncompressed_hex[2:].decode('hex'), curve=SECP256k1)

    try:
        res = vk.verify_digest(sig_bin, hash_hex.decode('hex'), sigdecode=sigdecode_der)
        return res
    except BadSignatureError:
        log.debug("Bad signature {}; not from {} on {}?".format(sigb64, pubkey_hex, hash_hex))
        return False
Example #2
0
    def _sign(self, pdata, sks, dump_json_data):
        if not isinstance(sks, list):
            sks = [sks]

        jheader = '{"alg": "ES256"}'
        jheader_b64 = base64url_encode(jheader)

        jpayload = json.dumps(pdata) if dump_json_data else pdata
        jpayload_b64 = base64url_encode(jpayload)

        pdata_sig = {'payload': jpayload_b64,
                     'signatures': []}

        for sk in sks:
            sig_string_b64 = jws.sign(jheader, jpayload, sk, is_json=True)

            order = sk.curve.order
            sig_string = base64url_decode(sig_string_b64)
            r, s = sigdecode_string(sig_string, order)
            sig_der = sigencode_der(r, s, order)
            sig_der_b64 = base64url_encode(sig_der)

            pdata_sig['signatures'].append({'protected': jheader_b64,
                                            'signature': sig_der_b64})

        return pdata_sig
Example #3
0
def sign(signing_hash, root_key, **kw):
	""" Signs hash with root_key. """

	number = utils.bytes_to_int(signing_hash)
	r, s = root_key.sign_number(number, **kw)
	r, s = _get_canonical_signature(r, s)
	return sigencode_der(r, s, None)
Example #4
0
def sign(signing_hash, root_key, **kw):
    """ Signs hash with root_key. """

    number = utils.bytes_to_int(signing_hash)
    r, s = root_key.sign_number(number, **kw)
    r, s = _get_canonical_signature(r, s)
    return sigencode_der(r, s, None)
Example #5
0
def test_ecdsa():
    # private key as an integer
    secret = 27777772222
    m = b"Nitin"

    # check if order and generator are in sync
    assert O == ec_scalar_mul(generator, order), "Generator seems off"

    sig = ecdsa_sign(secret, m)
    sig_hex = str(sig)

    pub = pub_key_from_priv(secret)
    assert pub
    print(f"pub key\n{pub_key_from_priv(secret)}\n")
    print(f"signature\n{sig_hex}\n")

    priv = SigningKey.from_secret_exponent(secret, SECP256k1, hashfunc=sha256)
    pub = priv.verifying_key
    pub.verify(bytes.fromhex(sig_hex), m)
    pytest.raises(BadSignatureError, pub.verify, bytes.fromhex(sig_hex),
                  b"wrongdata")
    with open("public.pem", 'wb') as f:
        f.write(pub.to_pem())
    with open("private.pem", 'wb') as f:
        f.write(priv.to_pem())
    with open('data.txt', "wb") as f:
        f.write(sha256(m).digest())
    with open('signature.der', "wb") as f:
        f.write(util.sigencode_der(sig.r, sig.s, order))
 def verify(self, msg, sig):
     order = (2 ** self.prepared_key.curve.key_size) - 1
     signature = sigencode_der(*sigdecode_string(sig, order), order=order)
     try:
         self.prepared_key.verify(signature, msg, ec.ECDSA(self.hash_alg()))
         return True
     except:
         return False
Example #7
0
 def verify(self, msg, sig):
     order = (2**self.prepared_key.curve.key_size) - 1
     signature = sigencode_der(*sigdecode_string(sig, order), order=order)
     try:
         self.prepared_key.verify(signature, msg, ec.ECDSA(self.hash_alg()))
         return True
     except Exception:
         return False
Example #8
0
 def test_der_encoding(self):
     # This simply verifies that the DER encoder from the ECDSA lib
     # we're using does the right thing and matches the output of the
     # DER encoder of ripple-lib.
     assert hexlify(sigencode_der(
         int('ff89083ed4923b3379381826339c614ac1cb79bf36b18c34d5e97784c5a5a9db', 16),
         int('cc4355eda8ce79c629fb53b0d19abc1b543d9f174626cf33b8a26254c63b22b7', 16),
         None)) == \
         b'3046022100ff89083ed4923b3379381826339c614ac1cb79bf36b18c34d5e97784c5a5a9db022100cc4355eda8ce79c629fb53b0d19abc1b543d9f174626cf33b8a26254c63b22b7'
Example #9
0
 def verify(self, msg, sig):
     order = (2 ** self.prepared_key.curve.key_size) - 1
     signature = sigencode_der(*sigdecode_string(sig, order), order=order)
     verifier = self.prepared_key.verifier(signature, ec.ECDSA(self.hash_alg()))
     verifier.update(msg)
     try:
         return verifier.verify()
     except:
         return False
Example #10
0
 def test_der_encoding(self):
     # This simply verifies that the DER encoder from the ECDSA lib
     # we're using does the right thing and matches the output of the
     # DER encoder of ripple-lib.
     assert hexlify(sigencode_der(
         int('ff89083ed4923b3379381826339c614ac1cb79bf36b18c34d5e97784c5a5a9db', 16),
         int('cc4355eda8ce79c629fb53b0d19abc1b543d9f174626cf33b8a26254c63b22b7', 16),
         None)) == \
         b'3046022100ff89083ed4923b3379381826339c614ac1cb79bf36b18c34d5e97784c5a5a9db022100cc4355eda8ce79c629fb53b0d19abc1b543d9f174626cf33b8a26254c63b22b7'
Example #11
0
    def digibox_sign(self, tx):
        try:

            change_keypath = None
            
            for i, txout in enumerate(tx.outputs):
                addr = tx.outputs[i][1]
                if self.is_change(addr):
                    change_keypath = self.address_id(addr)
            
            require_pass = True;
            for i, txin in enumerate(tx.inputs):
                signatures = filter(None, txin['signatures'])
                num = txin['num_sig']
                if len(signatures) == num:
                    # Continue if this txin is complete.
                    continue

                for x_pubkey in txin['x_pubkeys']:
                    print_error("Creating signature for", x_pubkey)
                    ii = tx.inputs[i]['x_pubkeys'].index(x_pubkey)
                    keypath = self.address_id(tx.inputs[i]['address'])
                    if True:
                        for_sig = tx.tx_for_sig(i)
                        msg = '{"sign": {"type":"transaction", "data":"%s", "keypath":"%s", "change_keypath":"%s"} }' % \
                               (for_sig, keypath, change_keypath)
                    else:
                        for_sig = Hash(tx.tx_for_sig(i).decode('hex'))
                        for_sig = for_sig.encode('hex')
                        msg = '{"sign": {"type":"hash", "data":"%s", "keypath":"%s"} }' % \
                               (for_sig, keypath)
           
                    reply = self.commander(msg, require_pass)

                    if reply==None: 
                        raise Exception("Could not sign transaction.")

                    if 'sign' in reply:
                        require_pass = False
                        print_error("Adding signature for", x_pubkey)
                        item = reply['sign']
                        tx.inputs[i]['x_pubkeys'][ii] = item['pubkey']
                        tx.inputs[i]['pubkeys'][ii] = item['pubkey']
                        r = int(item['sig'][:64], 16)
                        s = int(item['sig'][64:], 16)
                        sig = sigencode_der(r, s, generator_secp256k1.order())
                        tx.inputs[i]['signatures'][ii] = sig.encode('hex')
                    else:
                        raise Exception("Could not sign transaction.")
      

        except Exception as e:
            raise Exception(e) 
        else:
            print_error("is_complete", tx.is_complete())
            tx.raw = tx.serialize()
Example #12
0
    def digibox_sign(self, tx):
        try:
            change_keypath = None
            
            for i, txout in enumerate(tx.outputs):
                addr = tx.outputs[i][1]
                if self.is_change(addr):
                    change_keypath = self.address_id(addr)
            
            require_pass = True;
            for i, txin in enumerate(tx.inputs):
                signatures = filter(None, txin['signatures'])
                num = txin['num_sig']
                if len(signatures) == num:
                    # Continue if this txin is complete.
                    continue

                for x_pubkey in txin['x_pubkeys']:
                    print_error("Creating signature for", x_pubkey)
                    ii = tx.inputs[i]['x_pubkeys'].index(x_pubkey)
                    keypath = self.address_id(tx.inputs[i]['address'])
                    if True:
                        for_sig = tx.tx_for_sig(i)
                        msg = '{"sign": {"type":"transaction", "data":"%s", "keypath":"%s", "change_keypath":"%s"} }' % \
                               (for_sig, keypath, change_keypath)
                    else:
                        for_sig = Hash(tx.tx_for_sig(i).decode('hex'))
                        for_sig = for_sig.encode('hex')
                        msg = '{"sign": {"type":"hash", "data":"%s", "keypath":"%s"} }' % \
                               (for_sig, keypath)
           
                    reply = self.commander(msg, require_pass)

                    if reply==None: 
                        raise Exception("Could not sign transaction.")

                    if 'sign' in reply:
                        require_pass = False
                        print_error("Adding signature for", x_pubkey)
                        item = reply['sign']
                        tx.inputs[i]['x_pubkeys'][ii] = item['pubkey']
                        tx.inputs[i]['pubkeys'][ii] = item['pubkey']
                        r = int(item['sig'][:64], 16)
                        s = int(item['sig'][64:], 16)
                        sig = sigencode_der(r, s, generator_secp256k1.order())
                        tx.inputs[i]['signatures'][ii] = sig.encode('hex')
                    else:
                        raise Exception("Could not sign transaction.")
      

        except Exception as e:
            raise Exception(e) 
        else:
            print_error("is_complete", tx.is_complete())
            tx.raw = tx.serialize()
Example #13
0
def ecdsa_sign(key, signing_hash, **kw):
    """Sign the given data. The key is the secret returned by
    :func:`root_key_from_seed`.

    The data will be a binary coded transaction.
    """
    r, s = key.sign_number(int(signing_hash, 16), **kw)
    r, s = ecdsa_make_canonical(r, s)
    # Encode signature in DER format
    der_coded = sigencode_der(r, s, None)
    return der_coded
Example #14
0
def ecdsa_sign(key, signing_hash, **kw):
    """Sign the given data. The key is the secret returned by
    :func:`root_key_from_seed`.

    The data will be a binary coded transaction.
    """
    r, s = key.sign_number(int(signing_hash, 16), **kw)
    r, s = ecdsa_make_canonical(r, s)
    # Encode signature in DER format, as in
    # ``sjcl.ecc.ecdsa.secretKey.prototype.encodeDER``
    der_coded = sigencode_der(r, s, None)
    return der_coded
Example #15
0
    def _create_auth_token(self, sk, profile):
        jheader = '{"alg": "ES256"}'
        jheader_b64 = base64url_encode(jheader)

        body = {'id': profile.profile_id, 'timestamp': int(time.time())}
        jbody = json.dumps(body)
        jbody_b64 = base64url_encode(jbody)

        sig_string_b64 = jws.sign(jheader, jbody, sk, is_json=True)

        order = sk.curve.order
        sig_string = base64url_decode(sig_string_b64)
        r, s = sigdecode_string(sig_string, order)
        sig_der = sigencode_der(r, s, order)
        sig_der_b64 = base64url_encode(sig_der)

        return '{0}.{1}.{2}'.format(jheader_b64, jbody_b64, sig_der_b64)
Example #16
0
 def validate_signature(self, digest, signature):
     public_key = load_der_public_key(self.public_key, default_backend())
     if len(signature) == 64:
         hash = hashes.SHA256()
     elif len(signature) == 96:
         hash = hashes.SHA384()
     signature = binascii.hexlify(signature)
     r = int(signature[:int(len(signature)/2)], 16)
     s = int(signature[int(len(signature)/2):], 16)
     encoded_sig = sigencode_der(r, s, len(signature)*4)
     try:
         public_key.verify(encoded_sig, digest, ec.ECDSA(Prehashed(hash)))
         return True
     except InvalidSignature:
         # TODO Fixme. This is what is expected today on the outer calls. This should be implementation independent
         # but requires changing everything calling that
         from ecdsa import BadSignatureError
         raise BadSignatureError
Example #17
0
 def is_signed_by(self, channel: 'Output', ledger=None):
     if self.claim.unsigned_payload:
         pieces = [
             Base58.decode(self.get_address(ledger)),
             self.claim.unsigned_payload,
             self.claim.signing_channel_hash[::-1]
         ]
     else:
         pieces = [
             self.tx_ref.tx.inputs[0].txo_ref.hash,
             self.claim.signing_channel_hash,
             self.claim.to_message_bytes()
         ]
     digest = sha256(b''.join(pieces))
     public_key = load_der_public_key(channel.claim.channel.public_key_bytes, default_backend())
     hash = hashes.SHA256()
     signature = hexlify(self.claim.signature)
     r = int(signature[:int(len(signature)/2)], 16)
     s = int(signature[int(len(signature)/2):], 16)
     encoded_sig = sigencode_der(r, s, len(signature)*4)
     public_key.verify(encoded_sig, digest, ec.ECDSA(Prehashed(hash)))
     return True
Example #18
0
 def sign(self, data, deterministic=True):
     return sigencode_der(*self.raw_sign(data, deterministic))
Example #19
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') + '01'
                    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 #20
0
    def sign_transaction(self, tx, password, *, use_cache=False):
        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():
                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 #21
0
 def sign(self, data):
     return sigencode_der(*self.raw_sign(data))
Example #22
0
    def sign_transaction(self, tx, password):
        if tx.is_complete():
            return

        try:
            p2shTransaction = False
            derivations = self.get_tx_derivations(tx)
            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')).encode('hex')
                        hasharray_i = {'hash': inputHash, 'keypath': inputPath}
                        hasharray.append(hasharray_i)
                        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 signed['pubkey'] != 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')
                    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 #23
0
    def sign_transaction(self, tx, password):
        if tx.is_complete():
            return

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

            # Build hasharray from inputs
            for i, txin in enumerate(tx.inputs()):
                if txin.get('is_coinbase'):
                    self.give_error(
                        "Coinbase not supported")  # should never happen

                if len(txin['pubkeys']) > 1:
                    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.tx_for_sig(i).decode('hex')).encode('hex')
                        hasharray_i = {'hash': inputHash, 'keypath': inputPath}
                        hasharray.append(hasharray_i)
                        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 len(txinput['pubkeys']) < 2:
                        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
            msg = '{"sign": {"meta":"%s", "data":%s, "checkpub":%s} }' % \
                   (Hash(tx.serialize()).encode('hex'), json.dumps(hasharray), 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)
            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.")

            if len(reply['sign']) <> len(tx.inputs()):
                raise Exception("Incorrect number of transactions signed."
                                )  # Should never occur

            # Fill signatures
            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 = reply['sign'][i]
                    assert signed['pubkey'] == pubkey
                    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')
                    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 #24
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(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

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

            # Special serialization of the unsigned transaction for
            # the mobile verification app.
            class CustomTXSerialization(Transaction):
                @classmethod
                def input_script(self, txin, estimate_size=False):
                    if txin['type'] == 'p2pkh':
                        return Transaction.get_preimage_script(txin)
                    if txin['type'] == 'p2sh':
                        return '00' + push_script(Transaction.get_preimage_script(txin))
                    raise Exception("unsupported type %s" % txin['type'])
            tx_dbb_serialized = CustomTXSerialization(tx.serialize()).serialize()

            # 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} }' % \
                       (to_hexstr(Hash(tx_dbb_serialized)), json.dumps(hashes), json.dumps(pubkeyarray))).encode('utf8')
                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.")

                # multisig verification not working correctly yet
                if self.plugin.is_mobile_paired() and not p2shTransaction:
                    reply['tx'] = tx_dbb_serialized
                    self.plugin.comserver_post_notification(reply)

                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."))

                # Send twice, first returns an echo for smart verification
                reply = dbb_client.hid_send_encrypt(msg)
                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 = 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) + '01'
                    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()