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