def test_crypto(): pts = ((0x50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352, 0x2cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6), (0xa83b8de893467d3a88d959c0eb4032d9ce3bf80f175d4d9e75892a3ebb8ab7e5, 0x370f723328c24b7a97fe34063ba68f253fb08f8645d7c8b9a4ff98e3c29e7f0d), (0xf680556678e25084a82fa39e1b1dfd0944f7e69fddaa4e03ce934bd6b291dca0, 0x52c10b721d34447e173721fb0151c68de1106badb089fb661523b8302a9097f5), (0x241febb8e23cbd77d664a18f66ad6240aaec6ecdc813b088d5b901b2e285131f, 0x513378d9ff94f8d3d6c420bd13981df8cd50fd0fbd0cb5afabb3e66f2750026d)) for pt in pts: b = bytes([(pt[1] & 0x1) + 0x2]) + pt[0].to_bytes(32, 'big') b_full = bytes([0x04]) + pt[0].to_bytes(32, 'big') + pt[1].to_bytes(32, 'big') pk = PublicKey.from_bytes(b) assert pk.point.y == pt[1] assert b == pk.compressed_bytes assert b_full == bytes(pk) assert bytes(PublicKey.from_hex(pk.to_hex())) == b_full for i in range(10): pk = PrivateKey.from_random() assert PrivateKey.from_hex(pk.to_hex()).key == pk.key hd_priv = HDPrivateKey.master_key_from_entropy()[0] hd_priv2 = HDKey.from_hex(hd_priv.to_hex()) hd_pub = hd_priv.public_key hd_pub2 = HDKey.from_hex(hd_pub.to_hex()) assert isinstance(hd_priv2, HDPrivateKey) assert hd_priv2._key.key == hd_priv._key.key assert hd_priv2.chain_code == hd_priv.chain_code assert isinstance(hd_pub2, HDPublicKey) assert hd_pub2._key.point.x == hd_pub._key.point.x assert hd_pub2._key.point.y == hd_pub._key.point.y assert hd_pub2.chain_code == hd_pub.chain_code
def extract_sig_info(self): """ Returns the signature and corresponding public key. Returns: dict: Contains three keys: 'hash_type': Integer 'signature': The DER-encoded signature 'public_key': The bytes corresponding the public key. """ if len(self) != 2: raise TypeError("Script is not a P2PKH signature script") if not isinstance(self[0], bytes) or \ not isinstance(self[1], bytes): raise TypeError( "Signature script must contain two push operations.") try: sig_bytes = self[0] hash_type = sig_bytes[-1] Signature.from_der(sig_bytes[:-1]) except ValueError: raise TypeError("Signature does not appear to be valid") try: PublicKey.from_bytes(self[1]) except ValueError: raise TypeError("Public key does not appear to be valid") return dict(hash_type=hash_type, signature=sig_bytes, public_key=self[1])
def _op_checksig(self): """ The entire transaction's outputs, inputs, and script (from the most recently-executed OP_CODESEPARATOR to the end) are hashed. The signature used by OP_CHECKSIG must be a valid signature for this hash and public key. If it is, 1 is returned, 0 otherwise. """ self._check_stack_len(2) self._check_txn() pub_key_bytes = self._stack.pop() s = self._stack.pop() sig_der, hash_type = s[:-1], s[-1] pub_key = PublicKey.from_bytes(pub_key_bytes) sig = Signature.from_der(sig_der) txn_copy = self._txn._copy_for_sig(input_index=self._input_index, hash_type=hash_type, sub_script=self._sub_script) msg = bytes(txn_copy) + utils.pack_u32(hash_type) tx_digest = hashlib.sha256(msg).digest() verified = pub_key.verify(tx_digest, sig) self._stack.append(verified)
def test_crypto(): pts = ( (0x50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352, 0x2cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6), (0xa83b8de893467d3a88d959c0eb4032d9ce3bf80f175d4d9e75892a3ebb8ab7e5, 0x370f723328c24b7a97fe34063ba68f253fb08f8645d7c8b9a4ff98e3c29e7f0d), (0xf680556678e25084a82fa39e1b1dfd0944f7e69fddaa4e03ce934bd6b291dca0, 0x52c10b721d34447e173721fb0151c68de1106badb089fb661523b8302a9097f5), (0x241febb8e23cbd77d664a18f66ad6240aaec6ecdc813b088d5b901b2e285131f, 0x513378d9ff94f8d3d6c420bd13981df8cd50fd0fbd0cb5afabb3e66f2750026d)) for pt in pts: b = bytes([(pt[1] & 0x1) + 0x2]) + pt[0].to_bytes(32, 'big') b_full = bytes([0x04]) + pt[0].to_bytes(32, 'big') + pt[1].to_bytes( 32, 'big') pk = PublicKey.from_bytes(b) assert pk.point.y == pt[1] assert b == pk.compressed_bytes assert b_full == bytes(pk) assert bytes(PublicKey.from_hex(pk.to_hex())) == b_full for i in range(10): pk = PrivateKey.from_random() assert PrivateKey.from_hex(pk.to_hex()).key == pk.key hd_priv = HDPrivateKey.master_key_from_entropy()[0] hd_priv2 = HDKey.from_hex(hd_priv.to_hex()) hd_pub = hd_priv.public_key hd_pub2 = HDKey.from_hex(hd_pub.to_hex()) assert isinstance(hd_priv2, HDPrivateKey) assert hd_priv2._key.key == hd_priv._key.key assert hd_priv2.chain_code == hd_priv.chain_code assert isinstance(hd_pub2, HDPublicKey) assert hd_pub2._key.point.x == hd_pub._key.point.x assert hd_pub2._key.point.y == hd_pub._key.point.y assert hd_pub2.chain_code == hd_pub.chain_code
def _op_checkmultisig(self, partial=False): """ Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. Because public keys are not checked again if they fail any signature comparison, signatures must be placed in the scriptSig using the same order as their corresponding public keys were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack. """ self._check_stack_len(1) self._check_txn() num_keys = self._stack.pop() self._check_stack_len(num_keys) keys_bytes = [] for i in range(num_keys): keys_bytes.insert(0, self._stack.pop()) public_keys = [PublicKey.from_bytes(p) for p in keys_bytes] min_num_sigs = self._stack.pop() # Although "m" is the *minimum* number of required signatures, bitcoin # core only consumes "m" signatures and then expects an OP_0. This # means that if m < min_num_sigs <= n, bitcoin core will return a # script failure. See: # https://github.com/bitcoin/bitcoin/blob/0.10/src/script/interpreter.cpp#L840 # We will do the same. hash_types = set() sigs = [] for i in range(min_num_sigs): s = self._stack.pop() try: sig = Signature.from_der(s[:-1]) hash_types.add(s[-1]) sigs.insert(0, sig) except ValueError: if partial: # Put it back on stack self._stack.append(s) else: # If not a partial evaluation there are not enough # sigs rv = False break if len(hash_types) != 1: raise ScriptInterpreterError( "Not all signatures have the same hash type!") hash_type = hash_types.pop() txn_copy = self._txn._copy_for_sig(input_index=self._input_index, hash_type=hash_type, sub_script=self._sub_script) msg = bytes(txn_copy) + utils.pack_u32(hash_type) txn_digest = hashlib.sha256(msg).digest() # Now we verify last_match = -1 rv = True match_count = 0 for sig in sigs: matched_any = False for i, pub_key in enumerate(public_keys[last_match + 1:]): if pub_key.verify(txn_digest, sig): last_match = i match_count += 1 matched_any = True break if not matched_any: # Bail early if the sig couldn't be verified # by any public key rv = False break rv &= match_count >= min_num_sigs # Now make sure the last thing on the stack is OP_0 if len(self._stack) == 1: rv &= self._stack.pop() == b'' rv &= len(self._stack) == 0 else: rv = False self._stack.append(rv) if partial: self.match_count = match_count
def _op_checkmultisig(self, partial=False): """ Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. Because public keys are not checked again if they fail any signature comparison, signatures must be placed in the scriptSig using the same order as their corresponding public keys were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack. """ self._check_stack_len(1) self._check_txn() num_keys = self._stack.pop() self._check_stack_len(num_keys) keys_bytes = [] for i in range(num_keys): keys_bytes.insert(0, self._stack.pop()) public_keys = [PublicKey.from_bytes(p) for p in keys_bytes] min_num_sigs = self._stack.pop() # Although "m" is the *minimum* number of required signatures, bitcoin # core only consumes "m" signatures and then expects an OP_0. This # means that if m < min_num_sigs <= n, bitcoin core will return a # script failure. See: # https://github.com/bitcoin/bitcoin/blob/0.10/src/script/interpreter.cpp#L840 # We will do the same. hash_types = set() sigs = [] for i in range(min_num_sigs): s = self._stack.pop() try: sig = Signature.from_der(s[:-1]) hash_types.add(s[-1]) sigs.insert(0, sig) except ValueError: if partial: # Put it back on stack self._stack.append(s) else: # If not a partial evaluation there are not enough # sigs rv = False break if len(hash_types) != 1: raise ScriptInterpreterError("Not all signatures have the same hash type!") hash_type = hash_types.pop() txn_copy = self._txn._copy_for_sig(input_index=self._input_index, hash_type=hash_type, sub_script=self._sub_script) msg = bytes(txn_copy) + utils.pack_u32(hash_type) txn_digest = hashlib.sha256(msg).digest() # Now we verify last_match = -1 rv = True match_count = 0 for sig in sigs: matched_any = False for i, pub_key in enumerate(public_keys[last_match+1:]): if pub_key.verify(txn_digest, sig): last_match = i match_count += 1 matched_any = True break if not matched_any: # Bail early if the sig couldn't be verified # by any public key rv = False break rv &= match_count >= min_num_sigs # Now make sure the last thing on the stack is OP_0 if len(self._stack) == 1: rv &= self._stack.pop() == b'' rv &= len(self._stack) == 0 else: rv = False self._stack.append(rv) if partial: self.match_count = match_count