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 assemble(self): if not self.status == Resource.FAILED: try: self.status = Resource.ASSEMBLING stream = "" for part in self.parts: stream += part if self.encrypted: data = self.link.decrypt(stream) else: data = stream if self.compressed: self.data = bz2.decompress(data) else: self.data = data calculated_hash = RNS.Identity.fullHash(self.data+self.random_hash) if calculated_hash == self.hash: self.status = Resource.COMPLETE self.prove() else: self.status = Resource.CORRUPT except Exception as e: RNS.log("Error while assembling received resource.", RNS.LOG_ERROR) RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) self.status = Resource.CORRUPT if self.callback != None: self.link.resource_concluded(self) self.callback(self)
def loadPublicKey(self, key): try: self.pub_bytes = key self.pub = load_der_public_key(self.pub_bytes, backend=default_backend()) self.updateHashes() except Exception as e: RNS.log("Error while loading public key, the contained exception was: "+str(e), RNS.LOG_ERROR)
def client_connected(link): global latest_client_link RNS.log("Client connected") link.set_link_closed_callback(client_disconnected) link.set_packet_callback(server_packet_received) latest_client_link = link
def query_for_peer(source_hash): try: RNS.Transport.request_path(bytes.fromhex(source_hash)) except Exception as e: RNS.log( "Error while querying network for peer identity. The contained exception was: " + str(e), RNS.LOG_ERROR)
def __init__(self, app): RNS.log("Nomad Network Node starting...", RNS.LOG_VERBOSE) self.app = app self.identity = self.app.identity self.destination = RNS.Destination(self.identity, RNS.Destination.IN, RNS.Destination.SINGLE, "nomadnetwork", "node") self.last_announce = time.time() self.announce_interval = self.app.node_announce_interval self.job_interval = Node.JOB_INTERVAL self.should_run_jobs = True self.app_data = None self.name = self.app.node_name self.register_pages() self.register_files() self.destination.set_link_established_callback(self.peer_connected) if self.name == None: self.name = self.app.peer_settings["display_name"]+"'s Node" RNS.log("Node \""+self.name+"\" ready for incoming connections on "+RNS.prettyhexrep(self.destination.hash), RNS.LOG_VERBOSE) if self.app.node_announce_at_start: def delayed_announce(): time.sleep(Node.START_ANNOUNCE_DELAY) self.announce() da_thread = threading.Thread(target=delayed_announce) da_thread.setDaemon(True) da_thread.start() job_thread = threading.Thread(target=self.__jobs) job_thread.setDaemon(True) job_thread.start()
def write_to_directory(self, directory_path): file_name = RNS.hexrep(self.hash, delimit=False) file_path = directory_path + "/" + file_name try: if not self.packed: self.pack() container = { "state": self.state, "lxmf_bytes": self.packed, "transport_encrypted": self.transport_encrypted, "transport_encryption": self.transport_encryption } packed_container = msgpack.packb(container) file = open(file_path, "wb") file.write(packed_container) file.close() return file_path except Exception as e: RNS.log( "Error while writing LXMF message to file \"" + str(file_path) + "\". The contained exception was: " + str(e), RNS.LOG_ERROR) return None
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 file_received(self, request_receipt): try: file_name = request_receipt.response[0] file_data = request_receipt.response[1] file_destination = self.app.downloads_path + "/" + file_name counter = 0 while os.path.isfile(file_destination): counter += 1 file_destination = self.app.downloads_path + "/" + file_name + "." + str( counter) fh = open(file_destination, "wb") fh.write(file_data) fh.close() self.saved_file_name = file_destination.replace( self.app.downloads_path + "/", "", 1) self.status = Browser.DONE self.response_progress = 0 self.update_display() except Exception as e: RNS.log( "An error occurred while handling file response. The contained exception was: " + str(e), RNS.LOG_ERROR)
def load_private_key(self, prv_bytes): """ Load a private key into the instance. :param prv_bytes: The private key as *bytes*. :returns: True if the key was loaded, otherwise False. """ try: self.prv_bytes = prv_bytes[:Identity.KEYSIZE // 8 // 2] self.prv = X25519PrivateKey.from_private_bytes(self.prv_bytes) self.sig_prv_bytes = prv_bytes[Identity.KEYSIZE // 8 // 2:] self.sig_prv = Ed25519PrivateKey.from_private_bytes( self.sig_prv_bytes) self.pub = self.prv.public_key() self.pub_bytes = self.pub.public_bytes( encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw) self.sig_pub = self.sig_prv.public_key() self.sig_pub_bytes = self.sig_pub.public_bytes( encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw) self.update_hashes() return True except Exception as e: raise e RNS.log("Failed to load identity key", RNS.LOG_ERROR) RNS.log("The contained exception was: " + str(e), RNS.LOG_ERROR) return False
def download_local_file(self, path): try: file_path = self.app.filespath + path.replace("/file", "", 1) if os.path.isfile(file_path): file_name = os.path.basename(file_path) file_destination = self.app.downloads_path + "/" + file_name counter = 0 while os.path.isfile(file_destination): counter += 1 file_destination = self.app.downloads_path + "/" + file_name + "." + str( counter) fs = open(file_path, "rb") fd = open(file_destination, "wb") fd.write(fs.read()) fd.close() fs.close() self.saved_file_name = file_destination.replace( self.app.downloads_path + "/", "", 1) self.update_display() else: RNS.log( "The requested local download file does not exist: " + str(file_path), RNS.LOG_ERROR) self.status = Browser.DONE self.response_progress = 0 except Exception as e: RNS.log( "An error occurred while handling file response. The contained exception was: " + str(e), RNS.LOG_ERROR)
def filelist_timeout_job(): time.sleep(APP_TIMEOUT) global server_files if len(server_files) == 0: RNS.log("Timed out waiting for filelist, exiting") os._exit(0)
def decrypt(self, ciphertext): if self.prv != None: plaintext = None try: chunksize = Identity.DECRYPT_CHUNKSIZE chunks = int(math.ceil(len(ciphertext) / (float(chunksize)))) plaintext = b"" for chunk in range(chunks): start = chunk * chunksize end = (chunk + 1) * chunksize if (chunk + 1) * chunksize > len(ciphertext): end = len(ciphertext) plaintext += self.prv.decrypt( ciphertext[start:end], padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None)) except: RNS.log( "Decryption by " + RNS.prettyhexrep(self.hash) + " failed", RNS.LOG_VERBOSE) return plaintext else: raise KeyError( "Decryption failed because identity does not hold a private key" )
def confirmed(sender): try: display_name = e_name.get_edit_text() source_hash = bytes.fromhex(e_id.get_edit_text()) trust_level = DirectoryEntry.UNTRUSTED if r_unknown.state == True: trust_level = DirectoryEntry.UNKNOWN elif r_trusted.state == True: trust_level = DirectoryEntry.TRUSTED delivery = DirectoryEntry.DIRECT if r_propagated.state == True: delivery = DirectoryEntry.PROPAGATED entry = DirectoryEntry(source_hash, display_name, trust_level, preferred_delivery=delivery) self.app.directory.remember(entry) self.update_conversation_list() self.dialog_open = False self.app.ui.main_display.sub_displays.network_display.directory_change_callback( ) except Exception as e: RNS.log( "Could not save directory entry. The contained exception was: " + str(e), RNS.LOG_VERBOSE) if not dialog_pile.error_display: dialog_pile.error_display = True options = dialog_pile.options(height_type="pack") dialog_pile.contents.append((urwid.Text(""), options)) dialog_pile.contents.append((urwid.Text( ("error_text", "Could not save entry. Check your input."), align="center"), options))
def send(self): """ Sends the packet. :returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned. """ if not self.sent: if self.destination.type == RNS.Destination.LINK: if self.destination.status == RNS.Link.CLOSED: raise IOError("Attempt to transmit over a closed link") else: self.destination.last_outbound = time.time() self.destination.tx += 1 self.destination.txbytes += len(self.data) if not self.packed: self.pack() if RNS.Transport.outbound(self): return self.receipt else: RNS.log("No interfaces could process the outbound packet", RNS.LOG_ERROR) self.sent = False self.receipt = None return False else: raise IOError("Packet was already sent")
def received_announce(self, destination_hash, announced_identity, app_data): RNS.log("Received an announce from " + RNS.prettyhexrep(destination_hash)) RNS.log("The announce contained the following app data: " + app_data.decode("utf-8"))
def menu(): global server_files, server_link # Wait until we have a filelist while len(server_files) == 0: time.sleep(0.1) RNS.log("Ready!") time.sleep(0.5) global menu_mode menu_mode = "main" should_quit = False while (not should_quit): print_menu() while not menu_mode == "main": # Wait time.sleep(0.25) user_input = input() if user_input == "q" or user_input == "quit" or user_input == "exit": should_quit = True print("") else: if user_input in server_files: download(user_input) else: try: if 0 <= int(user_input) < len(server_files): download(server_files[int(user_input)]) except: pass if should_quit: server_link.teardown()
def create_keys(self): self.prv = X25519PrivateKey.generate() self.prv_bytes = self.prv.private_bytes( encoding=serialization.Encoding.Raw, format=serialization.PrivateFormat.Raw, encryption_algorithm=serialization.NoEncryption()) self.sig_prv = Ed25519PrivateKey.generate() self.sig_prv_bytes = self.sig_prv.private_bytes( encoding=serialization.Encoding.Raw, format=serialization.PrivateFormat.Raw, encryption_algorithm=serialization.NoEncryption()) self.pub = self.prv.public_key() self.pub_bytes = self.pub.public_bytes( encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw) self.sig_pub = self.sig_prv.public_key() self.sig_pub_bytes = self.sig_pub.public_bytes( encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw) self.update_hashes() RNS.log("Identity keys created for " + RNS.prettyhexrep(self.hash), RNS.LOG_VERBOSE)
def received_announce(destination_hash, announced_identity, app_data): app = nomadnet.NomadNetworkApp.get_shared_instance() if not destination_hash in app.ignored_list: destination_hash_text = RNS.hexrep(destination_hash, delimit=False) # Check if the announced destination is in # our list of conversations if destination_hash_text in [ e[0] for e in Conversation.conversation_list(app) ]: if app.directory.find(destination_hash): if Conversation.created_callback != None: Conversation.created_callback() else: if Conversation.created_callback != None: Conversation.created_callback() # Add the announce to the directory announce # stream logger app.directory.lxmf_announce_received(destination_hash, app_data) else: RNS.log( "Ignored announce from " + RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG)
def __mark_delivered(self, receipt=None): RNS.log("Received delivery notification for " + str(self), RNS.LOG_DEBUG) self.state = LXMessage.DELIVERED if self.__delivery_callback != None: self.__delivery_callback(self)
def send(self, content="", title=""): if self.send_destination: dest = self.send_destination source = self.app.lxmf_destination desired_method = LXMF.LXMessage.DIRECT if self.app.directory.preferred_delivery( dest.hash) == DirectoryEntry.PROPAGATED: if self.app.message_router.get_outbound_propagation_node( ) != None: desired_method = LXMF.LXMessage.PROPAGATED lxm = LXMF.LXMessage(dest, source, content, title=title, desired_method=desired_method) lxm.register_delivery_callback(self.message_notification) lxm.register_failed_callback(self.message_notification) if self.app.message_router.get_outbound_propagation_node() != None: lxm.try_propagation_on_fail = self.app.try_propagation_on_fail self.app.message_router.handle_outbound(lxm) message_path = Conversation.ingest(lxm, self.app, originator=True) self.messages.append(ConversationMessage(message_path)) return True else: RNS.log("Destination is not known, cannot create LXMF Message.", RNS.LOG_VERBOSE) return False
def lxmf_delivery(self, lxmf_data, destination_type=None): try: message = LXMessage.unpack_from_bytes(lxmf_data) if RNS.Reticulum.should_allow_unencrypted(): message.transport_encrypted = False message.transport_encryption = LXMessage.ENCRYPTION_DESCRIPTION_UNENCRYPTED else: if destination_type == RNS.Destination.SINGLE: message.transport_encrypted = True message.transport_encryption = LXMessage.ENCRYPTION_DESCRIPTION_RSA elif destination_type == RNS.Destination.GROUP: message.transport_encrypted = True message.transport_encryption = LXMessage.ENCRYPTION_DESCRIPTION_AES elif destination_type == RNS.Destination.LINK: message.transport_encrypted = True message.transport_encryption = LXMessage.ENCRYPTION_DESCRIPTION_EC else: message.transport_encrypted = False message.transport_encryption = None if self.__delivery_callback != None: self.__delivery_callback(message) return True except Exception as e: RNS.log("Could not assemble LXMF message from received data", RNS.LOG_NOTICE) RNS.log("The contained exception was: " + str(e), RNS.LOG_DEBUG) return False
def updateBitrate(self): try: self.bitrate = self.r_sf * ( (4.0/self.cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000 self.bitrate_kbps = round(self.bitrate/1000.0, 2) RNS.log(str(self)+" On-air bitrate is now "+str(self.bitrate_kbps)+ " kbps", RNS.LOG_DEBUG) except: self.bitrate = 0
def assemble(self): if not self.status == Resource.FAILED: try: self.status = Resource.ASSEMBLING stream = b"".join(self.parts) if self.encrypted: data = self.link.decrypt(stream) else: data = stream # Strip off random hash data = data[Resource.RANDOM_HASH_SIZE:] if self.compressed: self.data = bz2.decompress(data) else: self.data = data calculated_hash = RNS.Identity.fullHash(self.data + self.random_hash) if calculated_hash == self.hash: self.file = open(self.storagepath, "ab") self.file.write(self.data) self.file.close() self.status = Resource.COMPLETE self.prove() else: self.status = Resource.CORRUPT except Exception as e: RNS.log("Error while assembling received resource.", RNS.LOG_ERROR) RNS.log("The contained exception was: " + str(e), RNS.LOG_ERROR) self.status = Resource.CORRUPT self.link.resource_concluded(self) if self.segment_index == self.total_segments: if self.callback != None: self.data = open(self.storagepath, "rb") self.callback(self) try: self.data.close() os.unlink(self.storagepath) except Exception as e: RNS.log( "Error while cleaning up resource files, the contained exception was:", RNS.LOG_ERROR) RNS.log(str(e)) else: RNS.log( "Resource segment " + str(self.segment_index) + " of " + str(self.total_segments) + " received, waiting for next segment to be announced", RNS.LOG_DEBUG)
def prove(self, destination=None): if self.fromPacked and hasattr(self, "destination") and self.destination: if self.destination.identity and self.destination.identity.prv: self.destination.identity.prove(self, destination) elif self.fromPacked and hasattr(self, "link") and self.link: self.link.prove_packet(self) else: RNS.log("Could not prove packet associated with neither a destination nor a link", RNS.LOG_ERROR)
def link_established(self, link): self.status = Browser.LINK_ESTABLISHED if self.app.directory.should_identify_on_connect( self.destination_hash): RNS.log("Link established, identifying to remote system...", RNS.LOG_VERBOSE) self.link.identify(self.app.identity)
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 link_established(link): # We store a reference to the link # instance for later use global server_link server_link = link # Inform the user that the server is # connected RNS.log("Link established with server, enter some text to send, or \"quit\" to quit")
def fail_message(self, lxmessage): RNS.log(str(lxmessage) + " failed to send", RNS.LOG_DEBUG) self.pending_outbound.remove(lxmessage) self.failed_outbound.append(lxmessage) lxmessage.state = LXMessage.FAILED if lxmessage.failed_callback != None: lxmessage.failed_callback(lxmessage)