def __as_packet(self): if not self.packed: self.pack() if not self.__delivery_destination: raise ValueError( "Can't synthesize packet for LXMF message before delivery destination is known" ) if self.method == LXMessage.OPPORTUNISTIC: return RNS.Packet(self.__delivery_destination, self.packed[LXMessage.DESTINATION_LENGTH:]) elif self.method == LXMessage.DIRECT or self.method == LXMessage.PROPAGATED: return RNS.Packet(self.__delivery_destination, self.packed)
def validate_proof(self, packet): if self.initiator and len(packet.data) == RNS.Identity.SIGLENGTH // 8: signed_data = self.link_id + self.peer_pub_bytes + self.peer_sig_pub_bytes signature = packet.data[:RNS.Identity.SIGLENGTH // 8] if self.destination.identity.validate(signature, signed_data): self.rtt = time.time() - self.request_time self.attached_interface = packet.receiving_interface RNS.Transport.activate_link(self) RNS.log( "Link " + str(self) + " established with " + str(self.destination) + ", RTT is " + str(self.rtt), RNS.LOG_VERBOSE) rtt_data = umsgpack.packb(self.rtt) rtt_packet = RNS.Packet(self, rtt_data, context=RNS.Packet.LRRTT) RNS.log("Sending RTT packet", RNS.LOG_EXTREME) rtt_packet.send() self.had_outbound() self.status = Link.ACTIVE if self.callbacks.link_established != None: thread = threading.Thread( target=self.callbacks.link_established, args=(self, )) thread.setDaemon(True) thread.start() else: RNS.log( "Invalid link proof signature received by " + str(self) + ". Ignoring.", RNS.LOG_DEBUG)
def validateProof(self, packet): if self.initiator: peer_pub_bytes = packet.data[:Link.ECPUBSIZE] signed_data = self.link_id + peer_pub_bytes signature = packet.data[Link.ECPUBSIZE:RNS.Identity.KEYSIZE / 8 + Link.ECPUBSIZE] if self.destination.identity.validate(signature, signed_data): self.loadPeer(peer_pub_bytes) self.handshake() self.rtt = time.time() - self.request_time self.attached_interface = packet.receiving_interface RNS.Transport.activateLink(self) RNS.log( "Link " + str(self) + " established with " + str(self.destination) + ", RTT is " + str(self.rtt), RNS.LOG_VERBOSE) rtt_data = umsgpack.packb(self.rtt) rtt_packet = RNS.Packet(self, rtt_data, context=RNS.Packet.LRRTT) RNS.log("Sending RTT packet", RNS.LOG_EXTREME) rtt_packet.send() self.status = Link.ACTIVE if self.callbacks.link_established != None: self.callbacks.link_established(self) else: RNS.log( "Invalid link proof signature received by " + str(self), RNS.LOG_VERBOSE) # TODO: should we really do this, or just wait # for a valid one? Needs analysis. self.teardown()
def client_loop(): global server_link # Wait for the link to become active while not server_link: time.sleep(0.1) should_quit = False while not should_quit: try: print("> ", end=" ") text = input() # Check if we should quit the example if text == "quit" or text == "q" or text == "exit": should_quit = True server_link.teardown() # If not, send the entered text over the link if text != "": data = text.encode("utf-8") RNS.Packet(server_link, data).send() except Exception as e: should_quit = True server_link.teardown()
def __advertise_job(self): data = ResourceAdvertisement(self).pack() self.advertisement_packet = RNS.Packet(self.link, data, context=RNS.Packet.RESOURCE_ADV) while not self.link.ready_for_new_resource(): self.status = Resource.QUEUED sleep(0.25) try: self.advertisement_packet.send() self.last_activity = time.time() self.adv_sent = self.last_activity self.rtt = None self.status = Resource.ADVERTISED self.link.register_outgoing_resource(self) RNS.log( "Sent resource advertisement for " + RNS.prettyhexrep(self.hash), RNS.LOG_DEBUG) except Exception as e: RNS.log( "Could not advertise resource, the contained exception was: " + str(e), RNS.LOG_ERROR) self.cancel() return self.watchdog_job()
def client_loop(): global server_link # Wait for the link to become active while not server_link: time.sleep(0.1) should_quit = False while not should_quit: try: print("> ", end=" ") text = input() # Check if we should quit the example if text == "quit" or text == "q" or text == "exit": should_quit = True server_link.teardown() # If not, send the entered text over the link if text != "": data = text.encode("utf-8") if len(data) <= RNS.Link.MDU: RNS.Packet(server_link, data).send() else: RNS.log( "Cannot send this packet, the data size of "+ str(len(data))+" bytes exceeds the link packet MDU of "+ str(RNS.Link.MDU)+" bytes", RNS.LOG_ERROR ) except Exception as e: RNS.log("Error while sending data over the link: "+str(e)) should_quit = True server_link.teardown()
def client_connected(link): # Check if the served directory still exists if os.path.isdir(serve_path): RNS.log("Client connected, sending file list...") link.set_link_closed_callback(client_disconnected) # We pack a list of files for sending in a packet data = umsgpack.packb(list_files()) # Check the size of the packed data if len(data) <= RNS.Link.MDU: # If it fits in one packet, we will just # send it as a single packet over the link. list_packet = RNS.Packet(link, data) list_receipt = list_packet.send() list_receipt.set_timeout(APP_TIMEOUT) list_receipt.set_delivery_callback(list_delivered) list_receipt.set_timeout_callback(list_timeout) else: RNS.log("Too many files in served directory!", RNS.LOG_ERROR) RNS.log( "You should implement a function to split the filelist over multiple packets.", RNS.LOG_ERROR) RNS.log("Hint: The client already supports it :)", RNS.LOG_ERROR) # After this, we're just going to keep the link # open until the client requests a file. We'll # configure a function that get's called when # the client sends a packet with a file request. link.set_packet_callback(client_request) else: RNS.log("Client connected, but served path no longer exists!", RNS.LOG_ERROR) link.teardown()
def announce(self, app_data=None, path_response=False): destination_hash = self.hash random_hash = RNS.Identity.getRandomHash() signed_data = self.hash + self.identity.getPublicKey() + random_hash if app_data != None: signed_data += app_data signature = self.identity.sign(signed_data) # TODO: Check if this could be optimised by only # carrying the hash in the destination field, not # also redundantly inside the signed blob as here announce_data = self.hash + self.identity.getPublicKey( ) + random_hash + signature if app_data != None: announce_data += app_data if path_response: announce_context = RNS.Packet.PATH_RESPONSE else: announce_context = RNS.Packet.NONE RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context=announce_context).send()
def __init__(self, destination=None, owner=None, peer_pub_bytes=None): if destination != None and destination.type != RNS.Destination.SINGLE: raise TypeError( "Links can only be established to the \"single\" destination type" ) self.rtt = None self.callbacks = LinkCallbacks() self.resource_strategy = Link.ACCEPT_NONE self.outgoing_resources = [] self.incoming_resources = [] self.last_inbound = 0 self.last_outbound = 0 self.tx = 0 self.rx = 0 self.txbytes = 0 self.rxbytes = 0 self.default_timeout = Link.DEFAULT_TIMEOUT self.proof_timeout = self.default_timeout self.timeout_factor = Link.TIMEOUT_FACTOR self.keepalive = Link.KEEPALIVE self.watchdog_lock = False self.status = Link.PENDING self.type = RNS.Destination.LINK self.owner = owner self.destination = destination self.attached_interface = None self.__encryption_disabled = False if self.destination == None: self.initiator = False else: self.initiator = True self.prv = ec.generate_private_key(Link.CURVE, default_backend()) self.pub = self.prv.public_key() self.pub_bytes = self.pub.public_bytes( encoding=serialization.Encoding.DER, format=serialization.PublicFormat.SubjectPublicKeyInfo) if peer_pub_bytes == None: self.peer_pub = None self.peer_pub_bytes = None else: self.loadPeer(peer_pub_bytes) if (self.initiator): self.request_data = self.pub_bytes self.packet = RNS.Packet(destination, self.request_data, packet_type=RNS.Packet.LINKREQUEST) self.packet.pack() self.setLinkID(self.packet) RNS.Transport.registerLink(self) self.request_time = time.time() self.start_watchdog() self.packet.send() RNS.log( "Link request " + RNS.prettyhexrep(self.link_id) + " sent to " + str(self.destination), RNS.LOG_VERBOSE)
def cache_request_packet(packet): if len(packet.data) == RNS.Identity.HASHLENGTH / 8: packet_hash = RNS.hexrep(packet.data, delimit=False) path = RNS.Reticulum.cachepath + "/" + packet_hash if os.path.isfile(path): file = open(path, "r") raw = file.read() file.close() packet = RNS.Packet(None, raw)
def server_packet_received(message, packet): global latest_client_link text = message.decode("utf-8") RNS.log("Received data on the link: " + text) reply_text = "I got \"" + text + "\" from you" reply_data = reply_text.encode("utf-8") RNS.Packet(latest_client_link, reply_data).send()
def prove(self): if not self.status == Resource.FAILED: proof = RNS.Identity.fullHash(self.data + self.hash) proof_data = self.hash + proof proof_packet = RNS.Packet(self.link, proof_data, packet_type=RNS.Packet.PROOF, context=RNS.Packet.RESOURCE_PRF) proof_packet.send()
def prove(self): signed_data = self.link_id + self.pub_bytes signature = self.owner.identity.sign(signed_data) proof_data = self.pub_bytes + signature proof = RNS.Packet(self, proof_data, packet_type=RNS.Packet.PROOF, context=RNS.Packet.LRPROOF) proof.send()
def request_next(self): while self.receiving_part: sleep(0.001) if not self.status == Resource.FAILED: if not self.waiting_for_hmu: self.outstanding_parts = 0 hashmap_exhausted = Resource.HASHMAP_IS_NOT_EXHAUSTED requested_hashes = b"" offset = (1 if self.consecutive_completed_height > 0 else 0) i = 0 pn = self.consecutive_completed_height + offset search_start = pn for part in self.parts[search_start:search_start + self.window]: if part == None: part_hash = self.hashmap[pn] if part_hash != None: requested_hashes += part_hash self.outstanding_parts += 1 i += 1 else: hashmap_exhausted = Resource.HASHMAP_IS_EXHAUSTED pn += 1 if i >= self.window or hashmap_exhausted == Resource.HASHMAP_IS_EXHAUSTED: break hmu_part = bytes([hashmap_exhausted]) if hashmap_exhausted == Resource.HASHMAP_IS_EXHAUSTED: last_map_hash = self.hashmap[self.hashmap_height - 1] hmu_part += last_map_hash self.waiting_for_hmu = True requested_data = b"" request_data = hmu_part + self.hash + requested_hashes request_packet = RNS.Packet(self.link, request_data, context=RNS.Packet.RESOURCE_REQ) try: request_packet.send() self.last_activity = time.time() self.req_sent = self.last_activity self.req_resp = None except Exception as e: RNS.log( "Could not send resource request packet, cancelling resource", RNS.LOG_DEBUG) RNS.log("The contained exception was: " + str(e), RNS.LOG_DEBUG) self.cancel()
def prove_packet(self, packet): signature = self.sign(packet.packet_hash) # TODO: Hardcoded as explicit proof for now # if RNS.Reticulum.should_use_implicit_proof(): # proof_data = signature # else: # proof_data = packet.packet_hash + signature proof_data = packet.packet_hash + signature proof = RNS.Packet(self, proof_data, RNS.Packet.PROOF) proof.send()
def cache_request_packet(packet): if len(packet.data) == RNS.Identity.HASHLENGTH / 8: packet_hash = RNS.hexrep(packet.data, delimit=False) # TODO: There's some pretty obvious file access # issues here. Make sure this can't happen path = RNS.Reticulum.cachepath + "/" + packet_hash if os.path.isfile(path): file = open(path, "r") raw = file.read() file.close() packet = RNS.Packet(None, raw)
def server_packet_received(message, packet): global latest_client_link # When data is received over any active link, # it will all be directed to the last client # that connected. text = message.decode("utf-8") RNS.log("Received data on the link: "+text) reply_text = "I received \""+text+"\" over the link" reply_data = reply_text.encode("utf-8") RNS.Packet(latest_client_link, reply_data).send()
def prove(self, packet, destination=None): signature = self.sign(packet.packet_hash) if RNS.Reticulum.should_use_implicit_proof(): proof_data = signature else: proof_data = packet.packet_hash + signature if destination == None: destination = packet.generateProofDestination() proof = RNS.Packet(destination, proof_data, RNS.Packet.PROOF) proof.send()
def requestPath(destination_hash): path_request_data = destination_hash + RNS.Identity.getRandomHash() path_request_dst = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request") packet = RNS.Packet(path_request_dst, path_request_data, packet_type=RNS.Packet.DATA, transport_type=RNS.Transport.BROADCAST, header_type=RNS.Packet.HEADER_1) packet.send()
def teardown(self): if self.status != Link.PENDING and self.status != Link.CLOSED: teardown_packet = RNS.Packet(self, self.link_id, context=RNS.Packet.LINKCLOSE) teardown_packet.send() self.status = Link.CLOSED if self.initiator: self.teardown_reason = Link.INITIATOR_CLOSED else: self.teardown_reason = Link.DESTINATION_CLOSED self.link_closed()
def download(filename): global server_link, menu_mode, current_filename current_filename = filename # We just create a packet containing the # requested filename, and send it down the # link. request_packet = RNS.Packet(server_link, filename) request_packet.send() print("") print("Requested \""+filename+"\" from server, waiting for download to begin...") menu_mode = "download_started"
def cancel(self): if self.status < Resource.COMPLETE: self.status = Resource.FAILED if self.initiator: if self.link.status == RNS.Link.ACTIVE: cancel_packet = RNS.Packet(self.link, self.hash, context=RNS.Packet.RESOURCE_ICL) cancel_packet.send() self.link.cancel_outgoing_resource(self) else: self.link.cancel_incoming_resource(self) if self.callback != None: self.link.resource_concluded(self) self.callback(self)
def __advertise_job(self): data = ResourceAdvertisement(self).pack() self.advertisement_packet = RNS.Packet(self.link, data, context=RNS.Packet.RESOURCE_ADV) while not self.link.ready_for_new_resource(): self.status = Resource.QUEUED sleep(0.25) self.advertisement_packet.send() self.last_activity = time.time() self.adv_sent = self.last_activity self.rtt = None self.status = Resource.ADVERTISED self.link.register_outgoing_resource(self) self.watchdog_job()
def cache_request(packet_hash): RNS.log("Cache request for " + RNS.prettyhexrep(packet_hash), RNS.LOG_EXTREME) path = RNS.Reticulum.cachepath + "/" + RNS.hexrep(packet_hash, delimit=False) if os.path.isfile(path): file = open(path, "r") raw = file.read() Transport.inbound(raw) file.close() else: cache_request_packet = RNS.Packet( Transport.transport_destination(), packet_hash, context=RNS.Packet.CACHE_REQUEST)
def prove(self, packet, destination=None): signature = self.sign(packet.packet_hash) if RNS.Reticulum.should_use_implicit_proof(): proof_data = signature else: proof_data = packet.packet_hash + signature if destination == None: destination = packet.generate_proof_destination() proof = RNS.Packet(destination, proof_data, RNS.Packet.PROOF, attached_interface=packet.receiving_interface) proof.send()
def prove(self): if not self.status == Resource.FAILED: try: proof = RNS.Identity.fullHash(self.data + self.hash) proof_data = self.hash + proof proof_packet = RNS.Packet(self.link, proof_data, packet_type=RNS.Packet.PROOF, context=RNS.Packet.RESOURCE_PRF) proof_packet.send() except Exception as e: RNS.log("Could not send proof packet, cancelling resource", RNS.LOG_DEBUG) RNS.log("The contained exception was: " + str(e), RNS.LOG_DEBUG) self.cancel()
def broadcastLoop(destination): # Let the user know that everything is ready RNS.log("Broadcast example " + RNS.prettyhexrep(destination.hash) + " running, enter text and hit enter to broadcast (Ctrl-C to quit)") # We enter a loop that runs until the users exits. # If the user hits enter, we will send the information # that the user entered into the prompt. while True: print("> ", end="") entered = input() if entered != "": data = entered.encode("utf-8") packet = RNS.Packet(destination, data) packet.send()
def teardown(self): """ Closes the link and purges encryption keys. New keys will be used if a new link to the same destination is established. """ if self.status != Link.PENDING and self.status != Link.CLOSED: teardown_packet = RNS.Packet(self, self.link_id, context=RNS.Packet.LINKCLOSE) teardown_packet.send() self.had_outbound() self.status = Link.CLOSED if self.initiator: self.teardown_reason = Link.INITIATOR_CLOSED else: self.teardown_reason = Link.DESTINATION_CLOSED self.link_closed()
def download(filename): global server_link, menu_mode, current_filename, transfer_size, download_started current_filename = filename download_started = 0 transfer_size = 0 # We just create a packet containing the # requested filename, and send it down the # link. We also specify we don't need a # packet receipt. request_packet = RNS.Packet(server_link, filename.encode("utf-8"), create_receipt=False) request_packet.send() print("") print(("Requested \"" + filename + "\" from server, waiting for download to begin...")) menu_mode = "download_started"
def request_next(self): if not self.status == Resource.FAILED: if not self.waiting_for_hmu: self.outstanding_parts = 0 hashmap_exhausted = Resource.HASHMAP_IS_NOT_EXHAUSTED requested_hashes = b"" i = 0 pn = 0 for part in self.parts: if part == None: part_hash = self.hashmap[pn] if part_hash != None: requested_hashes += part_hash self.outstanding_parts += 1 i += 1 else: hashmap_exhausted = Resource.HASHMAP_IS_EXHAUSTED pn += 1 if i >= self.window or hashmap_exhausted == Resource.HASHMAP_IS_EXHAUSTED: break hmu_part = bytes([hashmap_exhausted]) if hashmap_exhausted == Resource.HASHMAP_IS_EXHAUSTED: last_map_hash = self.hashmap[self.hashmap_height - 1] hmu_part += last_map_hash self.waiting_for_hmu = True requested_data = b"" request_data = hmu_part + self.hash + requested_hashes request_packet = RNS.Packet(self.link, request_data, context=RNS.Packet.RESOURCE_REQ) request_packet.send() self.last_activity = time.time() self.req_sent = self.last_activity self.req_resp = None