def do_signing(wallet, req_keys, inputs):
    # Do the actual signing. We are trusting the sighash values.

    # Make a right subkey for each inputs
    wallets = {}
    for sp, (addr_check, ppair) in req_keys.items():
        w = wallet.subkey_for_path(sp)
        assert w.bitcoin_address() == addr_check
        assert w.public_pair() == tuple(ppair)
        wallets[sp] = w

    # Generate a signature for each input required
    sigs = []
    SIGHASH_ALL = 1
    order = ecdsa.generator_secp256k1.order()
    for sp, sighash in inputs:
        sighash_int = int(sighash, 16)
        r, s = ecdsa.sign(ecdsa.generator_secp256k1,
                          wallets[sp].secret_exponent(), sighash_int)
        if s + s > order:
            s = order - s
        sig = der.sigencode_der(r, s) + chr(SIGHASH_ALL)
        sigs.append((sig.encode('hex'), sighash, sp))

    return sigs
Exemple #2
0
def cosign_spend_request(xprvkey_or_wallet, req_keys, inputs, xpub_check):
    '''
        Sign the inputs of a transaction, given the sighashs and subkey paths for each input

    Args:
        xprvkey_or_wallet = 111-char base58 encoded serialization of BIP32 wallet
                            or pycoin.key.BIP32Node object (w/ private key)

        req_keys = dictionary: key is subpath ('a/b/c', but only 'a' for now as a string)
                    value is tuple: (address, public pair) ... optional checking data

        inputs = list of by transaction input: (subpath, sighash_all value)

    Returns:
        list of 3-tuples: (der-encoded signature, sighash, subpath)

    '''

    # We need just these features from pycoin <https://github.com/richardkiss/pycoin>
    from pycoin import ecdsa
    from pycoin.key.BIP32Node import BIP32Node
    from pycoin.tx.script import der

    # We need a BIP32 "wallet" for the root of all keys.
    if isinstance(xprvkey_or_wallet, basestring):
        wallet = BIP32Node.from_wallet_key(xprvkey_or_wallet.strip())
    else:
        wallet = xprvkey_or_wallet

    # Verify we are looking at the right extended private key
    check = wallet.hwif(as_private=False)[-len(xpub_check):]
    if check != xpub_check:
        raise ValueError("This private key isn't the right one for xpub...%s" %
                         xpub_check)

    # Make the right subkey for each inputs
    wallets = {}
    for sp, (addr_check, ppair) in req_keys.items():
        w = wallet.subkey_for_path(sp)
        assert w.bitcoin_address() == addr_check
        assert w.public_pair() == tuple(ppair)
        wallets[sp] = w

    # Generate a signature for each input required
    sigs = []
    SIGHASH_ALL = 1
    order = ecdsa.generator_secp256k1.order()
    for sp, sighash in inputs:
        sighash_int = int(sighash, 16)
        r, s = ecdsa.sign(ecdsa.generator_secp256k1,
                          wallets[sp].secret_exponent(), sighash_int)
        if s + s > order:
            s = order - s
        sig = der.sigencode_der(r, s) + chr(SIGHASH_ALL)
        sigs.append((sig.encode('hex'), sighash, sp))

    return sigs
Exemple #3
0
 def sign(self, h):
     """
     Return a der-encoded signature for a hash h.
     Will throw a RuntimeError if this key is not a private key
     """
     if not self.is_private():
         raise RuntimeError("Key must be private to be able to sign")
     val = from_bytes_32(h)
     r, s = secp256k1_generator.sign(self.secret_exponent(), val)
     return sigencode_der(r, s)
Exemple #4
0
 def sign(self, h):
     """
     Return a der-encoded signature for a hash h.
     Will throw a RuntimeError if this key is not a private key
     """
     if not self.is_private():
         raise RuntimeError("Key must be private to be able to sign")
     val = from_bytes_32(h)
     r, s = secp256k1_generator.sign(self.secret_exponent(), val)
     return sigencode_der(r, s)
Exemple #5
0
def cosign_spend_request(xprvkey_or_wallet, req_keys, inputs, xpub_check):
    """
        Sign the inputs of a transaction, given the sighashs and subkey paths for each input

    Args:
        xprvkey_or_wallet = 111-char base58 encoded serialization of BIP32 wallet
                            or pycoin.key.BIP32Node object (w/ private key)

        req_keys = dictionary: key is subpath ('a/b/c', but only 'a' for now as a string)
                    value is tuple: (address, public pair) ... optional checking data

        inputs = list of by transaction input: (subpath, sighash_all value)

    Returns:
        list of 3-tuples: (der-encoded signature, sighash, subpath)

    """

    # We need just these features from pycoin <https://github.com/richardkiss/pycoin>
    from pycoin import ecdsa
    from pycoin.key.BIP32Node import BIP32Node
    from pycoin.tx.script import der

    # We need a BIP32 "wallet" for the root of all keys.
    if isinstance(xprvkey_or_wallet, basestring):
        wallet = BIP32Node.from_wallet_key(xprvkey_or_wallet.strip())
    else:
        wallet = xprvkey_or_wallet

    # Verify we are looking at the right extended private key
    check = wallet.hwif(as_private=False)[-len(xpub_check) :]
    if check != xpub_check:
        raise ValueError("This private key isn't the right one for xpub...%s" % xpub_check)

    # Make the right subkey for each inputs
    wallets = {}
    for sp, (addr_check, ppair) in req_keys.items():
        w = wallet.subkey_for_path(sp)
        assert w.bitcoin_address() == addr_check
        assert w.public_pair() == tuple(ppair)
        wallets[sp] = w

    # Generate a signature for each input required
    sigs = []
    SIGHASH_ALL = 1
    order = ecdsa.generator_secp256k1.order()
    for sp, sighash in inputs:
        sighash_int = int(sighash, 16)
        r, s = ecdsa.sign(ecdsa.generator_secp256k1, wallets[sp].secret_exponent(), sighash_int)
        if s + s > order:
            s = order - s
        sig = der.sigencode_der(r, s) + chr(SIGHASH_ALL)
        sigs.append((sig.encode("hex"), sighash, sp))

    return sigs
def sigmake(a_key, a_hash_for_sig, a_sig_type=SIGHASH_ALL):
    """
    Signs a_hash_for_sig with a_key and returns a DER-encoded signature
    with a_sig_type appended.
    """
    order = generator_secp256k1.order()
    r, s = ecdsa_sign(generator_secp256k1, a_key.secret_exponent(), a_hash_for_sig)

    if s + s > order:
        s = order - s

    return sigencode_der(r, s) + bytes_from_int(a_sig_type)
def sigmake(a_key, a_hash_for_sig, a_sig_type=SIGHASH_ALL):
    """
    Signs a_hash_for_sig with a_key and returns a DER-encoded signature
    with a_sig_type appended.
    """
    order = secp256k1_generator.order()
    r, s = secp256k1_generator.sign(a_key.secret_exponent(), a_hash_for_sig)

    if s + s > order:
        s = order - s

    return sigencode_der(r, s) + int2byte(a_sig_type)
Exemple #8
0
    def __call__(self, tx_out_script, signature_hash, signature_type):
        """Figure out how to create a signature for the incoming transaction, and sign it.

        tx_out_script: the tx_out script that needs to be "solved"
        signature_hash: the bignum hash value of the new transaction reassigning the coins
        signature_type: always SIGHASH_ALL (1)
        """

        if signature_hash == 0:
            raise SolvingError("signature_hash can't be 0")

        tx_script = TxScript(tx_out_script)
        opcode_value_list = tx_script.match_script_to_templates()

        ba = bytearray()

        compressed = True
        for opcode, v in opcode_value_list:
            if opcode == opcodes.OP_PUBKEY:
                public_pair = sec_to_public_pair(v)
                bitcoin_address = public_pair_to_bitcoin_address(
                    public_pair, compressed=compressed)
            elif opcode == opcodes.OP_PUBKEYHASH:
                bitcoin_address = hash160_sec_to_bitcoin_address(v)
            else:
                raise SolvingError("can't determine how to sign this script")

            secret_exponent = self.wallet.getsecretexponent(bitcoin_address)

            r, s = ecdsa.sign(ecdsa.generator_secp256k1, secret_exponent,
                              signature_hash)
            sig = der.sigencode_der(r, s) + bytes_from_int(signature_type)
            ba += tools.compile(binascii.hexlify(sig).decode("utf8"))
            if opcode == opcodes.OP_PUBKEYHASH:
                public_pair = ecdsa.public_pair_for_secret_exponent(
                    ecdsa.generator_secp256k1, secret_exponent)
                ba += tools.compile(
                    binascii.hexlify(
                        public_pair_to_sec(
                            public_pair,
                            compressed=compressed)).decode("utf8"))

        return bytes(ba)
    def __call__(self, tx_out_script, signature_hash, signature_type):
        """Figure out how to create a signature for the incoming transaction, and sign it.

        tx_out_script: the tx_out script that needs to be "solved"
        signature_hash: the bignum hash value of the new transaction reassigning the coins
        signature_type: always SIGHASH_ALL (1)
        """

        if signature_hash == 0:
            raise SolvingError("signature_hash can't be 0")

        tx_script = TxScript(tx_out_script)
        opcode_value_list = tx_script.match_script_to_templates()

        ba = bytearray()

        compressed = True
        for opcode, v in opcode_value_list:
            if opcode == opcodes.OP_PUBKEY:
                public_pair = sec_to_public_pair(v)
                bitcoin_address = public_pair_to_bitcoin_address(public_pair, compressed=compressed)
            elif opcode == opcodes.OP_PUBKEYHASH:
                bitcoin_address = hash160_sec_to_bitcoin_address(v)
            else:
                raise SolvingError("can't determine how to sign this script")

            secret_exponent = self.wallet.getsecretexponent(bitcoin_address)

            r, s = ecdsa.sign(ecdsa.generator_secp256k1, secret_exponent, signature_hash)
            sig = der.sigencode_der(r, s) + bytes_from_int(signature_type)
            ba += tools.compile(binascii.hexlify(sig).decode("utf8"))
            if opcode == opcodes.OP_PUBKEYHASH:
                public_pair = ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, secret_exponent)
                ba += tools.compile(
                    binascii.hexlify(public_pair_to_sec(public_pair, compressed=compressed)).decode("utf8")
                )

        return bytes(ba)
def do_signing(wallet, req_keys, inputs):
    # Do the actual signing. We are trusting the sighash values.

    # Make a right subkey for each inputs
    wallets = {}
    for sp, (addr_check, ppair) in req_keys.items():
        w = wallet.subkey_for_path(sp)
        assert w.bitcoin_address() == addr_check
        assert w.public_pair() == tuple(ppair)
        wallets[sp] = w

    # Generate a signature for each input required
    sigs = []
    SIGHASH_ALL = 1
    order = ecdsa.generator_secp256k1.order()
    for sp, sighash in inputs:
        sighash_int = int(sighash, 16)
        r,s = ecdsa.sign(ecdsa.generator_secp256k1, wallets[sp].secret_exponent(), sighash_int)
        if s + s > order:
            s = order - s
        sig = der.sigencode_der(r, s) + chr(SIGHASH_ALL)
        sigs.append((sig.encode('hex'), sighash, sp))

    return sigs
def dummy_signature(sig_type):
    order = generator_secp256k1.order()
    r, s = order - 1, order // 2
    return der.sigencode_der(r, s) + bytes_from_int(sig_type)
Exemple #12
0
def dummy_signature(sig_type):
    order = generator_secp256k1.order()
    r, s = order - 1, order // 2
    return der.sigencode_der(r, s) + bytes_from_int(sig_type)
    n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141  #CONSTANT
    print('n: {}'.format(n))

    k = randint(0, 2**256)
    print('k: {}'.format(k))

    r = (k * g).pair()[0]
    print('r: {}'.format(r))

    s = (z + r * secret) * pow(k, n - 2, n) % n
    print('s: {}'.format(s))

    print('s less than n/2: {}'.format(s < n / 2))

    s_sig = hexlify(
        sigencode_der(r, s)
    )  #'3044022046536719d9dadf20a5933bcfa44995dfb5e0ef8f03dc2008a2e49111060909b40220648337a31f15fd5711ae25a9f3a8b68f4f05021254fb9566663fad86b6a2c870'
    print('s_sig: {}'.format(s_sig.decode()))

    r_hex = hex(
        r
    )  #'0x46536719d9dadf20a5933bcfa44995dfb5e0ef8f03dc2008a2e49111060909b4'
    print('r_hex: {}'.format(r_hex))

    s_hex = hex(
        s
    )  #'0x648337a31f15fd5711ae25a9f3a8b68f4f05021254fb9566663fad86b6a2c870'
    print('s_hex: {}'.format(s_hex))

    # THE END
Exemple #14
0
 def test_rfc6979(self):
     """
     Performs a test of the reference wallet's RFC6979 signatures against test vectors.
     """
     
     # Test vectors for RFC 6979 ECDSA (secp256k1, SHA-256).
     # Thanks to the Haskoin developer for these fully formed vectors.
     
     # (private key hex, private key WIF, message, r || r as hex, sig as DER)
     test_vectors = [
     ( 0x0000000000000000000000000000000000000000000000000000000000000001,
       "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn",
       "Everything should be made as simple as possible, but not simpler.",
       "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262",
       "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"
       ),
     ( 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140,
       "L5oLkpV3aqBjhki6LmvChTCV6odsp4SXM6FfU2Gppt5kFLaHLuZ9",
       "Equations are more important to me, because politics is for the present, but an equation is something for eternity.",
       "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5",
       "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"
       ),
     ( 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140,
       "L5oLkpV3aqBjhki6LmvChTCV6odsp4SXM6FfU2Gppt5kFLaHLuZ9",
       "Not only is the Universe stranger than we think, it is stranger than we can think.",
       "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283",
       "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"
       ),
     ( 0x0000000000000000000000000000000000000000000000000000000000000001,
       "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn",
       "How wonderful that we have met with a paradox. Now we have some hope of making progress.",
       "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3",
       "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3"
       ),
     ( 0x69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64,
       "KzmcSTRmg8Gtoq8jbBCwsrvgiTKRrewQXniAHHTf7hsten8MZmBB",
       "Computer science is no more about computers than astronomy is about telescopes.",
       "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6",
       "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6"
       ),
     ( 0x00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637,
       "KwDiBf89QgGbjEhKnhXJwe1E2mCa8asowBrSKuCaBV6EsPYEAFZ8",
       "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough",
       "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37",
       "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37"
       ),
     ( 0x000000000000000000000000000000000000000000056916d0f9b31dc9b637f3,
       "KwDiBf89QgGbjEhKnhXJuH7LrciVrZiib5S9h4knkymNojPUVsWN",
       "The question of whether computers can think is like the question of whether submarines can swim.",
       "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef",
       "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef"
       )
     ]
     
     for (secret_exponent, _, message, _, expected_sig) in test_vectors:
 
         h = hashlib.sha256(message.encode('utf-8')).digest()
         val = intbytes.from_bytes(h)        
         
         # This will use deterministic values of k based on 'val'
         r, s = ecdsa.sign(secp256k1.generator_secp256k1, secret_exponent, val)
             
         # Ensure that 's' is even to prevent attacks - see https://bitcointalk.org/index.php?topic=285142.msg3295518#msg3295518
         if s > (secp256k1.generator_secp256k1.order() / 2):
             s = secp256k1.generator_secp256k1.order() - s
         
         sig = der.sigencode_der(r, s)
         
         assert sig == bytes.fromhex(expected_sig), "ECDSA signature using RFC 6979 failed\nExpected: " + expected_sig + "\nActual:   " + self.hexstr(sig)
Exemple #15
0
    def test_rfc6979(self):
        """
        Performs a test of the reference wallet's RFC6979 signatures against test vectors.
        """

        # Test vectors for RFC 6979 ECDSA (secp256k1, SHA-256).
        # Thanks to the Haskoin developer for these fully formed vectors.

        # (private key hex, private key WIF, message, r || r as hex, sig as DER)
        test_vectors = [
            (0x0000000000000000000000000000000000000000000000000000000000000001,
             "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn",
             "Everything should be made as simple as possible, but not simpler.",
             "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262",
             "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"
             ),
            (0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140,
             "L5oLkpV3aqBjhki6LmvChTCV6odsp4SXM6FfU2Gppt5kFLaHLuZ9",
             "Equations are more important to me, because politics is for the present, but an equation is something for eternity.",
             "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5",
             "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"
             ),
            (0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140,
             "L5oLkpV3aqBjhki6LmvChTCV6odsp4SXM6FfU2Gppt5kFLaHLuZ9",
             "Not only is the Universe stranger than we think, it is stranger than we can think.",
             "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283",
             "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"
             ),
            (0x0000000000000000000000000000000000000000000000000000000000000001,
             "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn",
             "How wonderful that we have met with a paradox. Now we have some hope of making progress.",
             "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3",
             "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3"
             ),
            (0x69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64,
             "KzmcSTRmg8Gtoq8jbBCwsrvgiTKRrewQXniAHHTf7hsten8MZmBB",
             "Computer science is no more about computers than astronomy is about telescopes.",
             "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6",
             "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6"
             ),
            (0x00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637,
             "KwDiBf89QgGbjEhKnhXJwe1E2mCa8asowBrSKuCaBV6EsPYEAFZ8",
             "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough",
             "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37",
             "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37"
             ),
            (0x000000000000000000000000000000000000000000056916d0f9b31dc9b637f3,
             "KwDiBf89QgGbjEhKnhXJuH7LrciVrZiib5S9h4knkymNojPUVsWN",
             "The question of whether computers can think is like the question of whether submarines can swim.",
             "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef",
             "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef"
             )
        ]

        for (secret_exponent, _, message, _, expected_sig) in test_vectors:

            h = hashlib.sha256(message.encode('utf-8')).digest()
            val = intbytes.from_bytes(h)

            # This will use deterministic values of k based on 'val'
            r, s = ecdsa.sign(secp256k1.generator_secp256k1, secret_exponent,
                              val)

            # Ensure that 's' is even to prevent attacks - see https://bitcointalk.org/index.php?topic=285142.msg3295518#msg3295518
            if s > (secp256k1.generator_secp256k1.order() / 2):
                s = secp256k1.generator_secp256k1.order() - s

            sig = der.sigencode_der(r, s)

            assert sig == bytes.fromhex(
                expected_sig
            ), "ECDSA signature using RFC 6979 failed\nExpected: " + expected_sig + "\nActual:   " + self.hexstr(
                sig)