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 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 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 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(self): print "listening..." data, addr = self.sock.recvfrom(1024) print "received message[", addr, "]" ## 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 } try: packet_type = data[97] dispatch = response_types[packet_type] except KeyError: print " Unknown message type: " + data[97] return payload = data[98:] dispatch(payload)
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 eth_checksum_encode( addr ): # hex address o = '' v = int.from_bytes( keccak256( addr.encode('utf-8') ), 'big') for i, c in enumerate( addr ): if c in '0123456789': o += c else: o += c.upper() if (v & (1 << (255 - 4*i))) else c.lower() return Constants.ETH_PREFIX + o
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 wrap_packet(self, packet): """ Encode the packet. hash || signature || packet-type || packet-data """ # Append the packet type to the RLP encoding of the packet data payload = packet.packet_type + rlp.encode(packet.pack()) # Sign the hashed payload, use raw=True, cause we've already hashed, # and otherwise, the function would use its own hash function signature = self.private_key.ecdsa_sign_recoverable( keccak256(payload), raw=True, ) # Creates a tuple signature_serialized = self.private_key.ecdsa_recoverable_serialize(signature) payload = signature_serialized[0] + chr(signature_serialized[1] + payload) payload_hash = keccak256(payload) return payload_hash + payload
def from_pubkey(self, pubkey, coin): assert isinstance(pubkey, PublicKey) self.coin = coin if coin == "BCH": return self( hash160( pubkey.to_ser() ), coin) elif coin == "BTC": return self( hash160( pubkey.to_ser() ), coin) elif coin == "ETH": if pubkey.is_compressed(): pubkey.uncompress() return self( keccak256( pubkey.to_ser()[1:] )[-20:], coin ) else: AddressError("wrong coin")
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 lookup(self, target_key): target_id = keccak256(target_key) closest = [] while not closest: closest = self.closest(target_id, BUCKET_SIZE) if not closest: # add seed nodes for bn in self.server.boot_nodes: self.add_node(bn) asked = [self.self_node.node_id] pending_queries = 0 reply_queue = Queue() while True: for n in closest: if pending_queries >= KAD_ALPHA: break if n.node_id not in asked: asked.append(n.node_id) pending_queries += 1 gevent.spawn(self.find_neighbours, n, target_key, reply_queue) if pending_queries == 0: break ns = reply_queue.get() pending_queries -= 1 if ns: for node in ns: farther = find_farther_to_target_than( closest, target_id, node) if farther: closest.remove(farther) if len(closest) < BUCKET_SIZE: closest.append(node)
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 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 __str__(self): return "(FN " + binascii.b2a_hex(keccak256( self.target))[:8] + " " + str(self.timestamp) + ")"
def set_pubkey(self, pubkey): self.node_key = pubkey self.node_id = keccak256(self.node_key)
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)