def test_eth_sign(web3, skip_if_testrpc): skip_if_testrpc(web3) private_key_hex = '0x5e95384d8050109aab08c1922d3c230739bc16976553c317e5d0b87b59371f2a' private_key = decode_hex(private_key_hex) # This imports the private key into the running geth instance and unlocks # the account so that it can sign things. # `0xa5df35f30ba0ce878b1061ae086289adff3ba1e0` address = web3.personal.importRawKey(private_key, "password") web3.personal.unlockAccount(address, "password") assert add_0x_prefix(encode_hex(privtoaddr(private_key))) == add_0x_prefix(address) assert address == '0xa5df35f30ba0ce878b1061ae086289adff3ba1e0' # the data to be signed data = b'1234567890abcdefghijklmnopqrstuvwxyz' # the hash of the data `0x089c33d56ed10bd8b571a0f493cedb28db1ae0f40c6cd266243001528c06eab3` data_hash = web3.sha3(data, encoding=None) data_hash_bytes = decode_hex(data_hash) assert force_bytes(data_hash) == sha3(data) priv_key = PrivateKey(flags=ALL_FLAGS) priv_key.set_raw_privkey(private_key) # sanit check the extract_ecdsa_signer function works as expected. vector_sig = priv_key.ecdsa_sign_recoverable(data_hash_bytes, raw=True, digest=sha3_256) vector_sig_bytes, rec_id = priv_key.ecdsa_recoverable_serialize(vector_sig) vector_sig_bytes_full = vector_sig_bytes + force_bytes(chr(rec_id)) vector_address = force_text(extract_ecdsa_signer(data_hash_bytes, vector_sig_bytes_full)) assert vector_address == address # Now have geth sign some data. signature_hex = web3.eth.sign(address, data) signature_bytes = decode_hex(signature_hex) actual_signer = extract_ecdsa_signer(data_hash_bytes, signature_bytes) assert actual_signer == address # Verify the signature against the public key derived from the # original private key. It fails. rec_id = signature_bytes[64] if is_string(rec_id): rec_id = ord(rec_id) recoverable_signature = priv_key.ecdsa_recoverable_deserialize( signature_bytes[:64], rec_id, ) signature = priv_key.ecdsa_recoverable_convert(recoverable_signature) is_valid = priv_key.pubkey.ecdsa_verify( msg=data, raw_sig=signature, digest=sha3_256, ) assert is_valid
class PingServer(object): def __init__(self, my_endpoint): self.endpoint = my_endpoint ## get private key priv_key_file = open('priv_key', 'r') priv_key_serialized = priv_key_file.read() priv_key_file.close() self.priv_key = PrivateKey() self.priv_key.deserialize(priv_key_serialized) # bind socket self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind(('0.0.0.0', self.endpoint.udpPort)) # bind tcp socket self.tcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.tcpsock.bind(('0.0.0.0', self.endpoint.tcpPort)) self.tcpsock.listen(10) # p2p is the based network # so, when the p2p network close # the system need to send a quit blockchain msg to Discover server def __del__(self): pass # # hash || signature || packet-type || packet-data def wrap_packet(self, packet): payload = packet.packet_type + rlp.encode(packet.pack()) temp = keccak256(payload) sig = self.priv_key.ecdsa_sign_recoverable(temp, raw = True) sig_serialized = self.priv_key.ecdsa_recoverable_serialize(sig) # signature || packet-type || packet-data payload = sig_serialized[0] + chr(sig_serialized[1]) + payload # hash payload_hash = keccak256(payload) return payload_hash + payload def verifyData(self, data): # verify hash msg_hash = data[:32] if msg_hash != keccak256(data[32:]): print " First 32 bytes are not keccak256 hash of the rest." return else: print " Verified message hash." # verify signature signature = data[32:97] signed_data = data[97:] deserialized_sig = self.priv_key.ecdsa_recoverable_deserialize(signature[:64], ord(signature[64])) remote_pubkey = self.priv_key.ecdsa_recover(keccak256(signed_data), deserialized_sig, raw = True) pub = PublicKey() pub.public_key = remote_pubkey verified = pub.ecdsa_verify(keccak256(signed_data), pub.ecdsa_recoverable_convert(deserialized_sig), raw = True) if not verified: print " Signature invalid" return False else: print " Verified signature." return True def recvfrom(self): try: print "listening..." while True : data, addr = self.sock.recvfrom(1024) print "recvived mssage[", addr, "]" res = self.verifyData(data) if res: self.dispatch_data(data) except: traceback.print_exc() raise RuntimeError('recvfrom error') def recvtcpfrom(self, sock): try: print "tcp socket listening..." while (True): data = sock.recv(1024) print "【TCP】: get data, data's length is: %d"%len(data) res = self.verifyData(data) if res: self.dispatch_data(data) except: traceback.print_exc() raise RuntimeError('recvtcpfrom error') # 分裂一个进程 def tcp_listern(self): try: while True: sock, addr = self.tcpsock.accept() t = threading.Thread(target=self.recvtcpfrom, args=(sock,)) t.setDaemon(True) t.start() except: traceback.print_exc() raise RuntimeError('tcp_listern error') # 分裂一个线程来进行对tcp监听 def start_tcp_listern(self): t = threading.Thread(target=self.tcp_listern) t.setDaemon(True) t.start() def start_udp_listern(self): # udp listern listern_thread = self.udp_listen() listern_thread.start() def udp_listen(self): thread = threading.Thread(target= self.recvfrom,) thread.setDaemon(True) return thread def rawsendudpmsg(self, endpoint, message): self.sock.sendto(message, (endpoint.address.exploded, endpoint.udpPort)) # ping send a hello msg def ping(self, endpoint): ping = PingProtrol(self.endpoint, endpoint, time.time()) message = self.wrap_packet(ping) self.rawsendudpmsg(endpoint, message) def pong(self, pongNode): message = self.wrap_packet(pongNode) self.rawsendudpmsg(pongNode.to, message) def dispatch_data(self, data): response_types = { PingProtrol.packet_type: self.receive_ping, PongProtrol.packet_type: self.receive_pong, JoinProtrol.packet_type: self.receive_join, WelcomeProtrol.packet_type: self.receive_welcome, InitNodeProtrol.packet_type: self.receive_initnode, ChainMsgProtrol.packet_type: self.receive_msg } try: packet_type = data[97] dispatch = response_types[packet_type] except: print " Unknown message type: " + data[97] return payload = data[98:] dispatch(payload) # the function need overrided def receive_pong(self, payload): print " received Pong" print "", PongProtrol.unpack(rlp.decode(payload)) def receive_ping(self, payload): print " received Ping" print "", PingProtrol.unpack(rlp.decode(payload)) def receive_join(self, payload): print " received Join" print "", JoinProtrol.unpack(rlp.decode(payload)) def receive_welcome(self, payload): print " received welcome" print "", JoinProtrol.unpack(rlp.decode(payload)) def receive_initnode(self, payload): print " received initNode" print "", JoinProtrol.unpack(rlp.decode(payload)) def receive_msg(self, payload): print " received msg" print "", JoinProtrol.unpack(rlp.decode(payload)) # common function @staticmethod def SpecialEncode(endpoint): temp = { 'ip': endpoint.address.exploded, 'port': endpoint.tcpPort } return json.dumps(temp) @staticmethod def SpecialDecode(msg): return json.loads(msg)
def test_eth_sign(web3, skip_if_testrpc): skip_if_testrpc(web3) private_key_hex = '0x5e95384d8050109aab08c1922d3c230739bc16976553c317e5d0b87b59371f2a' private_key = decode_hex(private_key_hex) # This imports the private key into the running geth instance and unlocks # the account so that it can sign things. # address = '0xa5df35f30ba0ce878b1061ae086289adff3ba1e0' address = web3.personal.importRawKey(private_key, "password") web3.personal.unlockAccount(address, "password") assert add_0x_prefix(encode_hex( privtoaddr(private_key))) == add_0x_prefix(address) assert address == '0xa5df35f30ba0ce878b1061ae086289adff3ba1e0' # the data to be signed data = b'1234567890abcdefghijklmnopqrstuvwxyz' # the hash of the data `0x089c33d56ed10bd8b571a0f493cedb28db1ae0f40c6cd266243001528c06eab3` data_hash = web3.sha3(data, encoding=None) data_hash_bytes = decode_hex(data_hash) assert force_bytes(data_hash) == sha3(data) priv_key = PrivateKey(flags=ALL_FLAGS) priv_key.set_raw_privkey(private_key) # sanit check the extract_ecdsa_signer function works as expected. vector_sig = priv_key.ecdsa_sign_recoverable(data_hash_bytes, raw=True, digest=hashlib.sha3_256) vector_sig_bytes, rec_id = priv_key.ecdsa_recoverable_serialize(vector_sig) vector_sig_bytes_full = vector_sig_bytes + force_bytes(chr(rec_id)) vector_address = force_text( extract_ecdsa_signer(data_hash_bytes, vector_sig_bytes_full)) assert vector_address == address # Now have geth sign some data. signature_hex = web3.eth.sign(address, data) signature_bytes = decode_hex(signature_hex) # geth prefix message before signing geth_prefix_data = eth_message_prefix_hash(web3, data.decode()) actual_signer = extract_ecdsa_signer(force_bytes(geth_prefix_data), signature_bytes) assert actual_signer == address # Verify the signature against the public key derived from the # original private key. It fails. rec_id = signature_bytes[64] if is_string(rec_id): rec_id = ord(rec_id) recoverable_signature = priv_key.ecdsa_recoverable_deserialize( signature_bytes[:64], rec_id, ) signature = priv_key.ecdsa_recoverable_convert(recoverable_signature) is_valid = priv_key.pubkey.ecdsa_verify( msg=data, raw_sig=signature, digest=hashlib.sha3_256, ) assert is_valid
class TestIcxSigner(unittest.TestCase): def setUp(self): self.test_private_key = PrivateKey() self.signer = IcxSigner(self.test_private_key.private_key) m = hashlib.sha256() m.update(b'message_for_test') # prepare massage msg_hash self.hashed_message = m.digest() # check if signature which sign_recoverable method made is valid # use ecdsa_verify. before verify signature, convert sign (recoverable_sig -> normal_sig) # check secp256k1 doc: https://github.com/ludbb/secp256k1-py def test_sign_recoverable_verify_sig(self): # get sign, recovery sign, recovery_id = self.signer.sign_recoverable(self.hashed_message) # Convert recoverable sig to normal sig deserialized_recoverable_sig = self.test_private_key.ecdsa_recoverable_deserialize( sign, recovery_id) normal_sig = self.test_private_key.ecdsa_recoverable_convert( deserialized_recoverable_sig) # Check sig self.assertTrue( self.test_private_key.pubkey.ecdsa_verify(self.hashed_message, normal_sig, raw=True)) # Verify using invalid message m = hashlib.sha256() m.update(b'invalid message') invalid_message = m.digest() self.assertFalse( self.test_private_key.pubkey.ecdsa_verify(invalid_message, normal_sig, raw=True)) # Verify using invalid private key invalid_privateKey = PrivateKey() self.assertFalse( invalid_privateKey.pubkey.ecdsa_verify(self.hashed_message, normal_sig, raw=True)) def test_sign_base64_encode(self): # make signature encoded_sign = self.signer.sign(self.hashed_message) decoded_sign = base64.b64decode(encoded_sign) actual_id = int.from_bytes(decoded_sign[-1:], byteorder='big') actual_sig = decoded_sign[:len(decoded_sign) - 1] expected_signature, expected_recovery_id = self.signer.sign_recoverable( self.hashed_message) self.assertEqual(actual_id, expected_recovery_id) self.assertEqual(actual_sig, expected_signature) def test_key_from_key_store_get_private_key(self): # Invalid keystore file path self.assertRaises(KeyStoreException, key_from_key_store, './invalid_file_path', 'qwer1234%') # Invalid keystore password self.assertRaises(KeyStoreException, key_from_key_store, './tests/test_util/test_keystore', 'qwer12')
class PingServer(object): def __init__(self, my_endpoint, remote_endpoint, pingSleep, privKeyFile, kid): self.myEndpoint = my_endpoint self.theirEndpoint = remote_endpoint self.pingSleep = pingSleep self.kid = kid ## get private key pkf = open(privKeyFile, "r") priv_key_serialized = pkf.read() pkf.close() self.priv_key = PrivateKey() self.priv_key.deserialize(priv_key_serialized) ## init socket self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind( (self.myEndpoint.address.exploded, self.myEndpoint.udpPort)) def startPingLoop(self): self.pingLoopThread = threading.Thread(target=self._pingLoop).start() def startListening(self): print("Server " + str(self.kid) + ", start listening") self.listenThread = threading.Thread(target=self._listenLoop).start() def _pingLoop(self): print("pingLoop") while True: self.ping(self.theirEndpoint) self.findnode( self.theirEndpoint, 0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0 ) # self.neighbors(self.theirEndpoint, 0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0) time.sleep(self.pingSleep) def wrap_packet(self, packet): payload = packet.packet_type + rlp.encode(packet.pack()) sig = self.priv_key.ecdsa_sign_recoverable(keccak256(payload), raw=True) sig_serialized = self.priv_key.ecdsa_recoverable_serialize(sig) # print(str(type(sig_serialized[0])) + " " + str(type(sig_serialized[1])) + str(type(payload))) # print(sig_serialized[1]) payload = sig_serialized[0] + bytes([sig_serialized[1]]) + payload payload_hash = keccak256(payload) return payload_hash + payload def _listenLoop(self): print("Start Listening...") while True: data, addr = self.sock.recvfrom(2048) print("--------------- New Packet (ID: " + str(self.kid) + ") -------------") self.handlePacket(data, addr) def handlePacket(self, data, addr): # print("received message[" + str(addr) + "]") msg_hash = data[:32] # 32 Byte Hash raw_sig = data[32:97] # 64 Byte + 1 Byte Signature ptype = data[97] # 1 Byte packet_type pdata = data[98:] # Rest is rlp-encoded data decdata = rlp.decode(pdata) signedData = data[97:] # Verify hash if msg_hash != keccak256(data[32:]): print("Invalid message hash!") exit(0) # Verify signature deserialized_sig = self.priv_key.ecdsa_recoverable_deserialize( raw_sig[:64], raw_sig[64]) remote_pubkey = self.priv_key.ecdsa_recover(keccak256(signedData), deserialized_sig, raw=True) pub = PublicKey() pub.public_key = remote_pubkey verified = pub.ecdsa_verify( keccak256(signedData), pub.ecdsa_recoverable_convert(deserialized_sig), raw=True) if not verified: print("Signature invalid!") exit(0) else: print("Public Key: " + pub.serialize().hex()) packet_type = bytes([ptype]) if packet_type == PingPacket.packet_type: print("Got ping.") recv_ping = PingPacket.unpack(rlp.decode(pdata)) print(str(recv_ping)) # self.pong(msg_hash, recv_ping.To()) # TODO: Find out the correct endpoint self.pong(self.theirEndpoint, msg_hash) if packet_type == PongPacket.packet_type: print("Got pong.") recv_pong = PongPacket.unpack(decdata) print(str(recv_pong)) # self.ping(self.theirEndpoint) if packet_type == FindNodePacket.packet_type: print("Got FindNodePacket.") recv_findnode = FindNodePacket.unpack(rlp.decode(pdata)) target = recv_findnode.target print("Target: " + str(target.hex())) self.neighbors(self.theirEndpoint, target) if packet_type == NeighborsPacket.packet_type: print("Got NeighborsPacket.") recv_neighbors = NeighborsPacket.unpack(rlp.decode(pdata)) print("# Neighbors: " + str(len(recv_neighbors.neighbors))) def ping(self, theirEndpoint): ping = PingPacket(self.myEndpoint, theirEndpoint, time.time() + 60) message = self.wrap_packet(ping) print("Sending ping to: " + str(theirEndpoint)) self.sock.sendto( message, (theirEndpoint.address.exploded, theirEndpoint.udpPort)) def pong(self, theirEndpoint, echo): pong = PongPacket(theirEndpoint, echo, time.time() + 60) message = self.wrap_packet(pong) print("Sending pong to: " + str(theirEndpoint)) self.sock.sendto( message, (theirEndpoint.address.exploded, theirEndpoint.udpPort)) def findnode(self, theirEndpoint, target): findnode = FindNodePacket(target, time.time() + 60) message = self.wrap_packet(findnode) print("Sending FindNodePacket to: " + str(theirEndpoint)) self.sock.sendto( message, (theirEndpoint.address.exploded, theirEndpoint.udpPort)) def neighbors(self, theirEndpoint, target): # Compute some close neighbors on the fly neighbors = self.computeClosestNeighbors(target) packet = NeighborsPacket(neighbors, time.time() + 60) message = self.wrap_packet(packet) print("Sending NeighborsPacket to: " + str(theirEndpoint)) self.sock.sendto( message, (theirEndpoint.address.exploded, theirEndpoint.udpPort)) def computeClosestNeighbors(self, target): # Just return some hard coded IDs for now return [ Node( self.myEndpoint.address, self.myEndpoint.udpPort, self.myEndpoint.tcpPort, 0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0 ), Node( self.myEndpoint.address, self.myEndpoint.udpPort, self.myEndpoint.tcpPort, 0x7f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0 ), ]
class Server(object): def __init__(self, boot_nodes): # the endpoint of this server # this is a fake ip address used in packets. self.endpoint = EndPoint(u'127.0.0.1', 30303, 30303) # boot nodes self.boot_nodes = boot_nodes # hold all of pending self.pending_hold = [] # last pong received time of the special node id self.last_pong_received = {} # last ping received time of the special node id self.last_ping_received = {} # have the private key priv_key_file = open('priv_key', 'r') priv_key_serialized = priv_key_file.read() priv_key_file.close() self.priv_key = PrivateKey() self.priv_key.deserialize(priv_key_serialized) # routing table self.table = RoutingTable( Node(self.endpoint, pubkey_format(self.priv_key.pubkey)[1:]), self) # initialize UDP socket self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind(('0.0.0.0', self.endpoint.udpPort)) # set socket non-blocking mode self.sock.setblocking(0) def add_table(self, node): self.table.add_node(node) def add_pending(self, pending): pending.start() self.pending_hold.append(pending) return pending def run(self): gevent.spawn(self.clean_pending) gevent.spawn(self.listen) # wait forever evt = Event() evt.wait() def clean_pending(self): while True: for pending in list(self.pending_hold): if not pending.is_alive: self.pending_hold.remove(pending) time.sleep(K_REQUEST_TIMEOUT) def listen(self): LOGGER.info("{:5} listening...".format('')) while True: ready = select([self.sock], [], [], 1.0) if ready[0]: data, addr = self.sock.recvfrom(2048) # non-block data reading gevent.spawn(self.receive, data, addr) def receive(self, data, addr): """ macSize = 256 / 8 = 32 sigSize = 520 / 8 = 65 headSize = macSize + sigSize = 97 hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] shouldhash := crypto.Sha3(buf[macSize:]) """ # verify hash msg_hash = data[:32] assert msg_hash == keccak256( data[32:]), "First 32 bytes are not keccak256 hash of the rest" # verify signature signature = data[32:97] signed_data = data[97:] deserialized_sig = self.priv_key.ecdsa_recoverable_deserialize( signature[:64], ord(signature[64])) remote_pubkey = self.priv_key.ecdsa_recover(keccak256(signed_data), deserialized_sig, raw=True) pub = PublicKey() pub.public_key = remote_pubkey verified = pub.ecdsa_verify( keccak256(signed_data), pub.ecdsa_recoverable_convert(deserialized_sig), raw=True) assert verified, "Signature invalid" pubkey = pubkey_format(pub)[1:] hex_id = binascii.hexlify(keccak256(pubkey)) packet_type = data[97] payload = rlp.decode(data[98:]) if packet_type == PingNode.packet_type: # fake ip in packet payload[1][0] = addr[0] ping = PingNode.unpack(payload) if expired(ping): return self.receive_ping(addr, pubkey, ping, msg_hash) elif packet_type == Pong.packet_type: pong = Pong.unpack(payload) if expired(pong): return self.receive_pong(addr, pubkey, pong) elif packet_type == FindNeighbors.packet_type: fn = FindNeighbors.unpack(payload) if expired(fn): return self.receive_find_neighbors(addr, pubkey, fn) elif packet_type == Neighbors.packet_type: neighbours = Neighbors.unpack(payload) if expired(neighbours): return self.receive_neighbors(addr, pubkey, neighbours) else: assert False, " Unknown message type: {}".format(packet_type) def receive_pong(self, addr, pubkey, pong): remote_id = keccak256(pubkey) # response to ping last_pong_received = self.last_pong_received def match_callback(): # solicited reply last_pong_received[remote_id] = time.time() self.handle_reply(addr, pubkey, Pong.packet_type, pong, match_callback) def receive_ping(self, addr, pubkey, ping, msg_hash): remote_id = keccak256(pubkey) endpoint_to = EndPoint(addr[0], ping.endpoint_from.udpPort, ping.endpoint_from.tcpPort) pong = Pong(endpoint_to, msg_hash, time.time() + K_EXPIRATION) node_to = Node(pong.to, pubkey) # sending Pong response self.send_sock(pong, node_to) LOGGER.info("{:5} {}@{}:{} (Pong)".format( '---->', binascii.hexlify(node_to.node_id)[:8], addr[0], ping.endpoint_from.udpPort)) self.handle_reply(addr, pubkey, PingNode.packet_type, ping) node = Node(endpoint_to, pubkey) if time.time() - self.last_pong_received.get(remote_id, 0) > K_BOND_EXPIRATION: self.ping(node, lambda: self.add_table(node)) else: self.add_table(node) self.last_ping_received[remote_id] = time.time() def receive_find_neighbors(self, addr, pubkey, fn): remote_id = keccak256(pubkey) if time.time() - self.last_pong_received.get(remote_id, 0) > K_BOND_EXPIRATION: # lost origin or origin is off return target_id = keccak256(fn.target) closest = self.table.closest(target_id, BUCKET_SIZE) # sent neighbours in chunks ns = Neighbors([], time.time() + K_EXPIRATION) sent = False node_to = Node(EndPoint(addr[0], addr[1], addr[1]), pubkey) for c in closest: ns.nodes.append(c) if len(ns.nodes) == K_MAX_NEIGHBORS: self.send_sock(ns, node_to) LOGGER.info("{:5} {}@{}:{} {}".format( '---->', binascii.hexlify(node_to.node_id)[:8], addr[0], addr[1], ns)) ns.nodes = [] sent = True if len(ns.nodes) > 0 or not sent: self.send_sock(ns, node_to) LOGGER.info("{:5} {}@{}:{} {}".format( '---->', binascii.hexlify(node_to.node_id)[:8], addr[0], addr[1], ns)) def receive_neighbors(self, addr, pubkey, neighbours): # response to find neighbours self.handle_reply(addr, pubkey, Neighbors.packet_type, neighbours) def handle_reply(self, addr, pubkey, packet_type, packet, match_callback=None): remote_id = keccak256(pubkey) is_match = False for pending in self.pending_hold: if pending.is_alive and packet_type == pending.packet_type: if remote_id == pending.from_id: is_match = True pending.emit(packet) match_callback and match_callback() elif pending.ep is not None and pending.ep == addr: LOGGER.error('{:5} {}@{}:{} mismatch request {}'.format( '', binascii.hexlify(remote_id)[:8], addr[0], addr[1], binascii.hexlify(pending.from_id)[:8])) # is_match = True # pending.emit(packet) # match_callback and match_callback() # for bucket in self.table.buckets: # for node in bucket.nodes: # if node.node_id == pending.from_id: # node.set_pubkey(pubkey) if not is_match: LOGGER.warning('{:5} {}@{}:{} ({}) unsolicited response'.format( '<-//-', binascii.hexlify(remote_id)[:8], addr[0], addr[1], PACKET_TYPES.get(packet.packet_type))) def ping(self, node, callback=None): """ send a ping request to the given node and return instantly invoke callback while reply arrives """ ping = PingNode(self.endpoint, node.endpoint, time.time() + K_EXPIRATION) message = self.wrap_packet(ping) msg_hash = message[:32] def reply_call(chunks): if chunks.pop().echo == msg_hash: if callback is not None: callback() return True ep = (node.endpoint.address.exploded, node.endpoint.udpPort) pending = self.add_pending(Pending(node, Pong.packet_type, reply_call)) self.sock.sendto(message, ep) LOGGER.info("{:5} {}@{}:{} (Ping)".format( '---->', binascii.hexlify(node.node_id)[:8], ep[0], ep[1])) return pending def find_neighbors(self, node, target_key): """ send a find neighbours request to the given node and waits until the node has sent up to k neighbours """ node_id = node.node_id if time.time() - self.last_ping_received.get(node_id, 0) > K_BOND_EXPIRATION: send_ping = self.ping(node) receive_ping = self.add_pending( Pending(node, PingNode.packet_type, lambda _: True)) # wait until endpoint proof is finished gevent.joinall([send_ping, receive_ping]) fn = FindNeighbors(target_key, time.time() + K_EXPIRATION) def reply_call(chunks): num_received = 0 for neighbors in chunks: num_received += len(neighbors.nodes) if num_received >= BUCKET_SIZE: return True pending = self.add_pending( Pending(node, Neighbors.packet_type, reply_call, timeout=2)) self.send_sock(fn, node) ep = (node.endpoint.address.exploded, node.endpoint.udpPort) LOGGER.info("{:5} {}@{}:{} (FN {})".format( '---->', binascii.hexlify(node.node_id)[:8], ep[0], ep[1], binascii.hexlify(keccak256(fn.target))[:8])) # block to wait for neighbours ret = pending.get() if ret: neighbor_nodes = [] for chunk in ret: for n in chunk.nodes: neighbor_nodes.append(n) return neighbor_nodes def send_sock(self, packet, node): endpoint = node.endpoint message = self.wrap_packet(packet) ep = (endpoint.address.exploded, endpoint.udpPort) self.sock.sendto(message, ep) def wrap_packet(self, packet): """ UDP packets are structured as follows: hash || signature || packet-type || packet-data packet-type: single byte < 2**7 // valid values are [1,4] packet-data: RLP encoded list. Packet properties are serialized in the order in which they're defined. See packet-data below. Offset | 0 | MDC | Ensures integrity of packet, 65 | signature | Ensures authenticity of sender, `SIGN(sender-privkey, MDC)` 97 | type | Single byte in range [1, 4] that determines the structure of Data 98 | data | RLP encoded, see section Packet Data The packets are signed and authenticated. The sender's Node ID is determined by recovering the public key from the signature. sender-pubkey = ECRECOVER(Signature) The integrity of the packet can then be verified by computing the expected MDC of the packet as: MDC = SHA3(sender-pubkey || type || data) As an optimization, implementations may look up the public key by the UDP sending address and compute MDC before recovering the sender ID. If the MDC values do not match, the packet can be dropped. """ payload = packet.packet_type + rlp.encode(packet.pack()) sig = self.priv_key.ecdsa_sign_recoverable(keccak256(payload), raw=True) sig_serialized = self.priv_key.ecdsa_recoverable_serialize(sig) payload = sig_serialized[0] + chr(sig_serialized[1]) + payload payload_hash = keccak256(payload) return payload_hash + payload
class PingServer(object): def __init__(self, my_endpoint): self.endpoint = my_endpoint ## get private key priv_key_file = open('priv_key', 'r') priv_key_serialized = priv_key_file.read() priv_key_file.close() self.priv_key = PrivateKey() self.priv_key.deserialize(priv_key_serialized) ## init socket self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind(('0.0.0.0', self.endpoint.udpPort)) def wrap_packet(self, packet): payload = packet.packet_type + rlp.encode(packet.pack()) sig = self.priv_key.ecdsa_sign_recoverable(keccak256(payload), raw=True) sig_serialized = self.priv_key.ecdsa_recoverable_serialize(sig) payload = sig_serialized[0] + chr(sig_serialized[1]) + payload payload_hash = keccak256(payload) return payload_hash + payload def udp_listen(self): return threading.Thread(target=self.receive) def ping(self, endpoint): ping = PingNode(self.endpoint, endpoint, time.time() + 60) message = self.wrap_packet(ping) print "sending " + str(ping) self.sock.sendto(message, (endpoint.address.exploded, endpoint.udpPort)) def receive_pong(self, payload): print " received Pong" print "", Pong.unpack(rlp.decode(payload)) def receive_ping(self, payload): print " received Ping" print "", PingNode.unpack(rlp.decode(payload)) def receive(self): print "listening..." data, addr = self.sock.recvfrom(1024) print "received message[", addr, "]" ## decode packet below ## verify hash msg_hash = data[:32] if msg_hash != keccak256(data[32:]): print " First 32 bytes are not keccak256 hash of the rest." return else: print " Verified message hash." signature = data[32:97] signed_data = data[97:] deserialized_sig = self.priv_key.ecdsa_recoverable_deserialize( signature[:64], ord(signature[64])) remote_pubkey = self.priv_key.ecdsa_recover(keccak256(signed_data), deserialized_sig, raw=True) pub = PublicKey() pub.public_key = remote_pubkey verified = pub.ecdsa_verify( keccak256(signed_data), pub.ecdsa_recoverable_convert(deserialized_sig), raw=True) if not verified: print " Signature invalid" return else: print " Verified signature." response_types = { PingNode.packet_type: self.receive_ping, Pong.packet_type: self.receive_pong } try: packet_type = data[97] dispatch = response_types[packet_type] except KeyError: print " Unknown message type: " + data[97] return payload = data[98:] dispatch(payload)
class Server(object): def __init__(self, my_endpoint): self.endpoint = my_endpoint # 获取私钥 priv_key_file = open('priv_key', 'r') priv_key_serialized = priv_key_file.read() priv_key_file.close() self.priv_key = PrivateKey() self.priv_key.deserialize(priv_key_serialized) # 初始化套接字 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind(('0.0.0.0', self.endpoint.udpPort)) # set socket non-blocking mode self.sock.setblocking(0) def wrap_packet(self, packet): """ UDP packets are structured as follows: hash || signature || packet-type || packet-data packet-type: single byte < 2**7 // valid values are [1,4] packet-data: RLP encoded list. Packet properties are serialized in the order in which they're defined. See packet-data below. Offset | 0 | MDC | Ensures integrity of packet, 65 | signature | Ensures authenticity of sender, `SIGN(sender-privkey, MDC)` 97 | type | Single byte in range [1, 4] that determines the structure of Data 98 | data | RLP encoded, see section Packet Data The packets are signed and authenticated. The sender's Node ID is determined by recovering the public key from the signature. sender-pubkey = ECRECOVER(Signature) The integrity of the packet can then be verified by computing the expected MDC of the packet as: MDC = SHA3(sender-pubkey || type || data) As an optimization, implementations may look up the public key by the UDP sending address and compute MDC before recovering the sender ID. If the MDC values do not match, the packet can be dropped. """ payload = packet.packet_type + rlp.encode(packet.pack()) sig = self.priv_key.ecdsa_sign_recoverable(keccak256(payload), raw=True) sig_serialized = self.priv_key.ecdsa_recoverable_serialize(sig) payload = sig_serialized[0] + chr(sig_serialized[1]) + payload payload_hash = keccak256(payload) return payload_hash + payload def listen(self): print "listening..." while True: ready = select.select([self.sock], [], [], 1.0) if ready[0]: data, addr = self.sock.recvfrom(2048) print "received message[", addr, "]:" self.receive(data, addr) def listen_thread(self): thread = threading.Thread(target=self.listen) thread.daemon = True return thread def receive(self, data, addr): """ macSize = 256 / 8 = 32 sigSize = 520 / 8 = 65 headSize = macSize + sigSize = 97 hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] shouldhash := crypto.Sha3(buf[macSize:]) """ # verify hash msg_hash = data[:32] if msg_hash != keccak256(data[32:]): print " First 32 bytes are not keccak256 hash of the rest." return else: print " Verified message hash." # verify signature signature = data[32:97] signed_data = data[97:] deserialized_sig = self.priv_key.ecdsa_recoverable_deserialize(signature[:64], ord(signature[64])) remote_pubkey = self.priv_key.ecdsa_recover(keccak256(signed_data), deserialized_sig, raw=True) pub = PublicKey() pub.public_key = remote_pubkey verified = pub.ecdsa_verify(keccak256(signed_data), pub.ecdsa_recoverable_convert(deserialized_sig), raw=True) if not verified: print " Signature invalid" return else: print " Verified signature." response_types = { PingNode.packet_type: self.receive_ping, Pong.packet_type: self.receive_pong, FindNeighbors.packet_type: self.receive_find_neighbors, Neighbors.packet_type: self.receive_neighbors } try: packet_type = data[97] dispatch = response_types[packet_type] except KeyError: print " Unknown message type: " + data[97] return payload = data[98:] dispatch(payload, msg_hash, addr) def receive_pong(self, payload, msg_hash, addr): print " received Pong" print "", Pong.unpack(rlp.decode(payload)) def receive_ping(self, payload, msg_hash, addr): print " received Ping" ping = PingNode.unpack(rlp.decode(payload)) endpoint_to = EndPoint(addr[0], ping.endpoint_from.udpPort, ping.endpoint_from.tcpPort) pong = Pong(endpoint_to, msg_hash, time.time() + 60) print " sending Pong response: " + str(pong) self.send(pong, pong.to) def receive_find_neighbors(self, payload, msg_hash, addr): print " received FindNeighbors" print "", FindNeighbors.unpack(rlp.decode(payload)) def receive_neighbors(self, payload, msg_hash, addr): print " received Neighbors" print "", Neighbors.unpack(rlp.decode(payload)) def ping(self, endpoint): ping = PingNode(self.endpoint, endpoint, time.time() + 60) message = self.wrap_packet(ping) print "sending " + str(ping) self.sock.sendto(message, (endpoint.address.exploded, endpoint.udpPort)) def send(self, packet, endpoint): message = self.wrap_packet(packet) print "sending " + str(packet) self.sock.sendto(message, (endpoint.address.exploded, endpoint.udpPort))