def raw_parse(cls, s): '''Returns a HDPublicKey from a stream''' # first 4 bytes are the version version = s.read(4) # check that the version is one of the TESTNET or MAINNET # public keys, if not raise a ValueError if version in (TESTNET_XPUB, TESTNET_YPUB, TESTNET_ZPUB): testnet = True elif version in (MAINNET_XPUB, MAINNET_YPUB, MAINNET_ZPUB): testnet = False else: raise ValueError('not an xpub, ypub or zpub: {} {}'.format(s, version)) # the next byte is depth depth = byte_to_int(s.read(1)) # next 4 bytes are the parent_fingerprint parent_fingerprint = s.read(4) # next 4 bytes is the child number in big-endian child_number = big_endian_to_int(s.read(4)) # next 32 bytes are the chain code chain_code = s.read(32) # last 33 bytes should be the SEC point = S256Point.parse(s.read(33)) # return an instance of the class return cls( point=point, chain_code=chain_code, depth=depth, parent_fingerprint=parent_fingerprint, child_number=child_number, testnet=testnet, )
def test_pubpoint(self): # write a test that tests the public point for the following points = ( # secret, x, y (7, 0x5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc, 0x6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da ), (1485, 0xc982196a7466fbbbb0e27a940b6af926c1a74d5ad07128c82824a11b5398afda, 0x7a91f9eae64438afb9ce6448a1c133db2d8fb9254e4546b6f001637d50901f55 ), (2**128, 0x8f68b9d2f63b5f339239c1ad981f162ee88c5678723ea3351b7b444c9ec4c0da, 0x662a9f2dba063986de1d90c2b6be215dbbea2cfe95510bfdf23cbf79501fff82 ), (2**240 + 2**31, 0x9577ff57c8234558f293df502ca4f09cbc65a6572c842b39b366f21717945116, 0x10b49c67fa9365ad7b90dab070be339a1daf9052373ec30ffae4f72d5e66d053 ), ) for secret, x, y in points: # initialize the secp256k1 point point = S256Point(x, y) # check that te secret*G is the same as the point self.assertEqual(secret * G, point)
def op_checkmultisig(stack, z): # m of n multi-sig if len(stack) < 1: return False n = decode_num(stack.pop()) if len(stack) < n+1: return False sec_pubkeys = [] for _ in range(n): sec_pubkeys.append(stack.pop()) m = decode_num(stack.pop()) if len(stack) < m+1: return False der_signatures = [] for _ in range(m): der_signatures.append(stack.pop()[:-1]) stack.pop() # Off-by-One bug try: pubPoints = [S256Point.parse(sec) for sec in sec_pubkeys] sigs = [Signature.parse(der) for der in der_signatures] for sig in sigs: if len(pubPoints) == 0: return False while pubPoints: pubPoint = pubPoints.pop(0) if pubPoint.verify(z, sig): break stack.append(encode_num(1)) except (ValueError, SyntaxError): return False return True
def op_checksig(stack, z): # check that there are at least 2 elements on the stack if len(stack) < 2: return False # the top element of the stack is the SEC pubkey sec_pubkey = stack.pop() print(f'stack sec:{sec_pubkey.hex()}') # the next element of the stack is the DER signature der_signature = stack.pop()[:-1] print(f'stack der:{der_signature}') # take off the last byte of the signature as that's the hash_type hash_type = der_signature[-1:] print(f'hash_type:{hash_type.hex()}') # parse the serialized pubkey and signature into objects try: point = S256Point.parse(sec_pubkey) der_signature = Signature.parse(der_signature) print(f'point:{point}') print(f'sig:{der_signature}') except (ValueError, SyntaxError) as e: return False if point.verify(z, der_signature): stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True
def op_checksig(stack, z): # check that there are at least 2 elements on the stack if len(stack) < 2: return False # the top element of the stack is the SEC pubkey pubkey_bin = stack.pop() # the next element of the stack is the DER signature sig_bin = stack.pop() # take off the last byte of the signature as that's the hash_type hash_type = sig_bin[-1] sig_bin = sig_bin[:-1] # parse the serialized pubkey and signature into objects pubkey = S256Point.parse(pubkey_bin) sig = Signature.parse(sig_bin) # verify the signature using S256Point.verify() valid = pubkey.verify(z, sig) # push an encoded 1 or 0 depending on whether the signature verified stack.append(encode_num(1) if valid else encode_num(0)) return True
def op_checksig(stack, z): # check that there are at least 2 elements on the stack if len(stack) < 2: return False # the top element of the stack is the SEC pubkey sec_pubkey = stack.pop() # the next element of the stack is the DER signature # take off the last byte of the signature as that's the hash_type # We will explain what this means when we explain validation of transactions next!!! Please be patient! # More at https://en.bitcoin.it/wiki/OP_CHECKSIG der_signature = stack.pop()[:-1] # parse the serialized pubkey and signature into objects try: point = S256Point.parse(sec_pubkey) sig = Signature.parse(der_signature) except (ValueError, SyntaxError): #print('Parse fail', point) return False # verify the signature using S256Point.verify() # push an encoded 1 or 0 depending on whether the signature verified if point.verify(z, sig): stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True
def derive_pub_child(self, i): if i >= HARD_CAP: return ValueError("Chosen i is not in range [0, 2**32-1]") # Not quite sure if this is true # if int.from_bytes(self.child_num, 'big') >= SOFT_CAP: # raise TypeError("Hardened Public Keys cannot derive child keys. Use Extended Private key.") if i >= SOFT_CAP: raise TypeError( "Hardened Keys cannot be be derived from Extended Pub Keys. Use Extended Private key." ) else: ii = hmac.new(self.chaincode, self.key + i.to_bytes(4, 'big'), digestmod=sha512).digest() fingerprint = hash160(self.key)[:4] # edge case: invalid keys key_num = int.from_bytes(ii[:32], 'big') point = key_num * G if key_num >= N or point.x is None: raise ValueError(INVALID_KEY_MSG) child_key = point + S256Point.parse(self.key) child_chaincode = ii[32:] #assemble new xpub child_xpub = self.pfx child_xpub += (self.depth[0] + 1).to_bytes(1, 'big') child_xpub += fingerprint child_xpub += i.to_bytes(4, 'big') child_xpub += child_chaincode child_xpub += child_key.sec() checksum = hash256(child_xpub)[:4] child_xpub += checksum return self.__class__(child_xpub)
def verify_input(self, input_index): '''Returns whether the input has a valid signature''' # get the relevant input tx_in = self.tx_ins[input_index] # get the number of signatures required. This is available in tx_in.script_sig.num_sigs_required() sigs_required = tx_in.script_sig.num_sigs_required() # iterate over the sigs required and check each signature for sig_num in range(sigs_required): # get the point from the sec format sec = tx_in.sec_pubkey(index=sig_num) # get the sec_pubkey at current signature index point = S256Point.parse(sec) # get the der sig and hash_type from input # get the der_signature at current signature index der, hash_type = tx_in.der_signature(index=sig_num) # get the signature from der format signature = Signature.parse(der) # get the hash to sign if tx_in.is_segwit(): h160 = hash160(tx_in.script_sig.redeem_script()) if h160 != tx_in.script_pubkey(self.testnet).elements[1]: return False pubkey_h160 = tx_in.script_sig.redeem_script()[-20:] if pubkey_h160 != point.h160(): return False z = self.sig_hash_bip143(input_index, hash_type) else: z = self.sig_hash(input_index, hash_type) # use point.verify on the hash to sign and signature if not point.verify(z, signature): return False return True
def test_example_4(self): sec = bytes.fromhex('0349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278a') der = bytes.fromhex('3045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed') z = 0x27e0c5994dec7824e56dec6b2fcb342eb7cdb0d0957c2fce9882f715e85d81a6 point = S256Point.parse(sec) signature = Signature.parse(der) self.assertTrue(point.verify(z, signature))
def test_verify_ch3_example8(self): z = 0xbc62d4b80d9e36da29c16c5d4d9f11731f36052c72401a76c23c0fb5a9b74423 r = 0x37206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c6 s = 0x8ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec px = 0x04519fac3d910ca7e7138f7013706f619fa8f033e6ec6e09370ea38cee6a7574 py = 0x82b51eab8c27c66e26c858a079bcdf4f1ada34cec420cafc7eac1a42216fb6c4 point = S256Point(px, py) self.assertTrue(point.verify(z, Signature(r, s)))
def test_example_8(self): point = S256Point(0x5CBDF0646E5DB4EAA398F365F2EA7A0E3D419B7E0330E39CE92BDDEDCAC4F9BC, 0x6AEBCA40BA255960A3178D6D861A54DBA813D0B813FDE7B5A5082628087264DA) uncompressed = b'\x04' + point.x.num.to_bytes(32, 'big') + point.y.num.to_bytes(32, 'big') if point.y.num % 2 == 1: compressed = b'\x03' + point.x.num.to_bytes(32, 'big') else: compressed = b'\x02' + point.x.num.to_bytes(32, 'big') self.assertEqual(uncompressed.hex(), '045cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da') self.assertEqual(compressed.hex(), '025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc')
def test_exercise_2(self): der = bytes.fromhex( '304402201f62993ee03fca342fcb45929993fa6ee885e00ddad8de154f268d98f083991402201e1ca12ad140c04e0e022c38f7ce31da426b8009d02832f0b44f39a6b178b7a1' ) sec = bytes.fromhex( '0204519fac3d910ca7e7138f7013706f619fa8f033e6ec6e09370ea38cee6a7574' ) z = int.from_bytes(hash256(b'ECDSA is awesome!'), 'big') sig = Signature.parse(der) point = S256Point.parse(sec) self.assertTrue(point.verify(z, sig))
def op_checksig(stack, z): if len(stack) < 2: return False sec_pubkey = stack.pop() der_signature = stack.pop()[:-1] point = S256Point.parse(sec_pubkey) sig = Signature.parse(der_signature) if point.verify(z, sig): stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True
def op_checkmultisig(stack, z): if len(stack) < 1: return False # n is the number of public keys n = decode_num(stack.pop()) if len(stack) < n + 1: return False # get all the public keys into a list. pubkeys = [] for _ in range(n): pubkeys.append(stack.pop()) if len(stack) < 1: return False # m is the number of signatures m = decode_num(stack.pop()) # m + 2 because of the additional element at the bottom of the stack that is added. if len(stack) < m + 1: return False # get all the signatures. signatures = [] for _ in range(m): # take off last byte, which is the hashtype signatures.append(stack.pop()[:-1]) if len(stack) < 1: return False # we remove the last element from the stack (the one included because the off by one error) stack.pop() # verify all the signatures against all pubkeys. If a signature isn't valid for any pubkeys, fail. try: sigs = [Signature.parse(signature) for signature in signatures] points = [S256Point.parse(pubkey) for pubkey in pubkeys] except (ValueError, SyntaxError) as e: LOGGER.info(e) return False # variable to count the number of valid signatures. count = 0 # in the next loop, we check that each signature is valid for a pubkey. while len(points) > 0: # point is popped so each signature can only be valid for 1 point. point = points.pop() for sig in sigs: # if the signature is valid for this pubkey, increase the count and break from the while # to check next signature. if point.verify(z, sig): count += 1 # if the number of valid signatures is m = each signature is valid for some pubkey, then script is valid. if count == m: stack.append(encode_num(1)) # else, script should fail. else: stack.append(encode_num(0)) return True
def getaddress(x, y, testnet=True, compressed=True): p = S256Point(x, y) comp = p.sec(compressed) h160 = hash160(comp) prefix = b'\00' if testnet: prefix = b'\x6f' else: prefix = b'\00' raw = prefix + h160 checksum = double_sha256(raw)[:4] total = raw + checksum return encode_base58(total)
def connect_peer(host, port, node): sock = SocketClient(host, port) sock.connect() peer_address = sock.receive() sock.send(str.encode(node.address)) peer_pub_key = sock.receive() sock.send(node.public_key.sec()) peer_pub_key_point = S256Point.parse(peer_pub_key) peer_sym_key = node.secret * peer_pub_key_point return Peer(sock, peer_address, peer_pub_key_point, peer_sym_key)
def __init__(self, xpub): self.pfx = xpub[:4] self.depth = xpub[4:5] self.parent = xpub[5:9] self.child_num = xpub[9:13] self.chaincode = xpub[13:45] self.key = xpub[45:-4] self.checksum = xpub[-4:] if not self.check_sum(): raise ConfigurationError("Invalid Checksum for ExtendedPrivKey") try: #TODO use assert point = S256Point.parse(self.key) except ValueError: raise ConfigurationError("Point is not on the curve, invalid key.")
def listen_for_new_peer(host, port, node): sock = SocketServer(host, port) sock.listen() sock.send(str.encode(node.address)) peer_address = sock.receive() sock.send(node.public_key.sec()) peer_pub_key = sock.receive() peer_pub_key_point = S256Point.parse(peer_pub_key) peer_sym_key = node.secret * peer_pub_key_point return Peer(sock, peer_address, peer_pub_key_point, peer_sym_key)
def op_checksig(stack, z): if len(stack) < 2: return False pub_sec = stack.pop() sig_der = stack.pop()[:-1] # except hash_type try: pub_point = S256Point.parse(pub_sec) sig = Signature.parse(sig_der) except (ValueError, SyntaxError) as e: return False if pub_point.verify(z, sig): stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True