Compare commits

...

31 Commits

Author SHA1 Message Date
Mark Qvist da13ee9cb9 Updated manual 2021-08-21 21:27:56 +02:00
Mark Qvist f719d44db5 Transport optimisations 2021-08-21 20:23:36 +02:00
Mark Qvist af890d91d2 Fixed race condition in outbound handling packet filter 2021-08-21 19:42:01 +02:00
Mark Qvist 242941fec4 Updated readme 2021-08-21 15:33:14 +02:00
Mark Qvist 0f79197945 Updated docs 2021-08-21 15:19:24 +02:00
Mark Qvist 212518a345 Implemented requests and responses of arbitrary sizes using resources. 2021-08-21 14:52:31 +02:00
Mark Qvist 1dc6655017 Implemented request and response API 2021-08-20 23:29:06 +02:00
Mark Qvist 69930e5652 Updated default config 2021-08-20 11:23:35 +02:00
Mark Qvist 2b8b95da2b Added config options for TCP server interface binding to network interface instead of IP. 2021-08-19 20:13:53 +02:00
Mark Qvist 6382409194 Added config options for UDP interface binding to network interface instead of IP. 2021-08-19 19:56:35 +02:00
Mark Qvist 4fd3d26714 Fixed UDP broadcast echo packets not being filtered. 2021-08-19 17:05:07 +02:00
Mark Qvist 8b6870fad8 Updated docs 2021-08-19 14:11:22 +02:00
Mark Qvist 384a7db974 Implemented link peer identification 2021-08-19 14:10:37 +02:00
Mark Qvist 772ae44ab8 Updated readme and docs 2021-07-25 23:48:18 +02:00
Mark Qvist d326df6c5a Cleanup 2021-05-20 23:31:26 +02:00
Mark Qvist 4269c48293 Updated readme 2021-05-20 23:16:19 +02:00
Mark Qvist 719764fd81 Updated documentation 2021-05-20 22:35:10 +02:00
Mark Qvist 5ccbc825fd Updated examples 2021-05-20 22:31:09 +02:00
Mark Qvist ad67c553d7 Added exception when trying to remember an invalid public key 2021-05-20 22:30:54 +02:00
Mark Qvist d68cfaa8f7 Optimised link establishment 2021-05-20 20:32:08 +02:00
Mark Qvist cf9934810b Updated documentation 2021-05-20 18:37:59 +02:00
Mark Qvist e8ca88377a Updated documentation 2021-05-20 18:37:12 +02:00
Mark Qvist bf410e006f Updated docs 2021-05-20 17:18:38 +02:00
Mark Qvist db527b6759 Optimised announces to 151 bytes 2021-05-20 16:56:08 +02:00
Mark Qvist 9c995b33dd Updated documentation 2021-05-20 16:06:12 +02:00
Mark Qvist f18fb35aba Updated documentation 2021-05-20 15:31:58 +02:00
Mark Qvist ce405b9252 Migrated all asymmetric crypto operations to ECIES on Curve25519. 2021-05-20 15:31:38 +02:00
Mark Qvist 7f5625a526 Cleanup 2021-05-20 13:38:57 +02:00
Mark Qvist e8fb435f00 Updated link example 2021-05-20 13:37:48 +02:00
Mark Qvist f880edbeb8 Store GROUP destination symmetric key as bytes instead of base64 2021-05-20 12:44:12 +02:00
Mark Qvist 2b97c89566 Updated docs 2021-05-20 10:28:58 +02:00
45 changed files with 2896 additions and 884 deletions
+1 -1
View File
@@ -39,7 +39,7 @@ def program_setup(configpath, channel=None):
# We specify a callback that will get called every time # We specify a callback that will get called every time
# the destination receives data. # the destination receives data.
broadcast_destination.packet_callback(packet_callback) broadcast_destination.set_packet_callback(packet_callback)
# Everything's ready! # Everything's ready!
# Let's hand over control to the main loop # Let's hand over control to the main loop
+4 -4
View File
@@ -52,7 +52,7 @@ def server(configpath):
# Tell the destination which function in our program to # Tell the destination which function in our program to
# run when a packet is received. We do this so we can # run when a packet is received. We do this so we can
# print a log message when the server receives a request # print a log message when the server receives a request
echo_destination.packet_callback(server_callback) echo_destination.set_packet_callback(server_callback)
# Everything's ready! # Everything's ready!
# Let's Wait for client requests or user input # Let's Wait for client requests or user input
@@ -170,12 +170,12 @@ def client(destination_hexhash, configpath, timeout=None):
# the packet times out. # the packet times out.
if timeout != None: if timeout != None:
packet_receipt.set_timeout(timeout) packet_receipt.set_timeout(timeout)
packet_receipt.timeout_callback(packet_timed_out) packet_receipt.set_timeout_callback(packet_timed_out)
# We can then set a delivery callback on the receipt. # We can then set a delivery callback on the receipt.
# This will get automatically called when a proof for # This will get automatically called when a proof for
# this specific packet is received from the destination. # this specific packet is received from the destination.
packet_receipt.delivery_callback(packet_delivered) packet_receipt.set_delivery_callback(packet_delivered)
# Tell the user that the echo request was sent # Tell the user that the echo request was sent
RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash)) RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash))
@@ -189,7 +189,7 @@ def client(destination_hexhash, configpath, timeout=None):
# receives a proof packet. # receives a proof packet.
def packet_delivered(receipt): def packet_delivered(receipt):
if receipt.status == RNS.PacketReceipt.DELIVERED: if receipt.status == RNS.PacketReceipt.DELIVERED:
rtt = receipt.rtt() rtt = receipt.get_rtt()
if (rtt >= 1): if (rtt >= 1):
rtt = round(rtt, 3) rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds" rttstring = str(rtt)+" seconds"
+15 -11
View File
@@ -65,7 +65,7 @@ def server(configpath, path):
# We configure a function that will get called every time # We configure a function that will get called every time
# a new client creates a link to this destination. # a new client creates a link to this destination.
server_destination.link_established_callback(client_connected) server_destination.set_link_established_callback(client_connected)
# Everything's ready! # Everything's ready!
# Let's Wait for client requests or user input # Let's Wait for client requests or user input
@@ -102,7 +102,7 @@ def client_connected(link):
if os.path.isdir(serve_path): if os.path.isdir(serve_path):
RNS.log("Client connected, sending file list...") RNS.log("Client connected, sending file list...")
link.link_closed_callback(client_disconnected) link.set_link_closed_callback(client_disconnected)
# We pack a list of files for sending in a packet # We pack a list of files for sending in a packet
data = umsgpack.packb(list_files()) data = umsgpack.packb(list_files())
@@ -114,8 +114,8 @@ def client_connected(link):
list_packet = RNS.Packet(link, data) list_packet = RNS.Packet(link, data)
list_receipt = list_packet.send() list_receipt = list_packet.send()
list_receipt.set_timeout(APP_TIMEOUT) list_receipt.set_timeout(APP_TIMEOUT)
list_receipt.delivery_callback(list_delivered) list_receipt.set_delivery_callback(list_delivered)
list_receipt.timeout_callback(list_timeout) list_receipt.set_timeout_callback(list_timeout)
else: else:
RNS.log("Too many files in served directory!", RNS.LOG_ERROR) 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("You should implement a function to split the filelist over multiple packets.", RNS.LOG_ERROR)
@@ -125,7 +125,7 @@ def client_connected(link):
# open until the client requests a file. We'll # open until the client requests a file. We'll
# configure a function that get's called when # configure a function that get's called when
# the client sends a packet with a file request. # the client sends a packet with a file request.
link.packet_callback(client_request) link.set_packet_callback(client_request)
else: else:
RNS.log("Client connected, but served path no longer exists!", RNS.LOG_ERROR) RNS.log("Client connected, but served path no longer exists!", RNS.LOG_ERROR)
link.teardown() link.teardown()
@@ -135,7 +135,12 @@ def client_disconnected(link):
def client_request(message, packet): def client_request(message, packet):
global serve_path global serve_path
try:
filename = message.decode("utf-8") filename = message.decode("utf-8")
except Exception as e:
filename = None
if filename in list_files(): if filename in list_files():
try: try:
# If we have the requested file, we'll # If we have the requested file, we'll
@@ -254,18 +259,18 @@ def client(destination_hexhash, configpath):
# We expect any normal data packets on the link # We expect any normal data packets on the link
# to contain a list of served files, so we set # to contain a list of served files, so we set
# a callback accordingly # a callback accordingly
link.packet_callback(filelist_received) link.set_packet_callback(filelist_received)
# We'll also set up functions to inform the # We'll also set up functions to inform the
# user when the link is established or closed # user when the link is established or closed
link.link_established_callback(link_established) link.set_link_established_callback(link_established)
link.link_closed_callback(link_closed) link.set_link_closed_callback(link_closed)
# And set the link to automatically begin # And set the link to automatically begin
# downloading advertised resources # downloading advertised resources
link.set_resource_strategy(RNS.Link.ACCEPT_ALL) link.set_resource_strategy(RNS.Link.ACCEPT_ALL)
link.resource_started_callback(download_began) link.set_resource_started_callback(download_began)
link.resource_concluded_callback(download_concluded) link.set_resource_concluded_callback(download_concluded)
menu() menu()
@@ -497,7 +502,6 @@ def download_concluded(resource):
saved_filename = current_filename saved_filename = current_filename
if resource.status == RNS.Resource.COMPLETE: if resource.status == RNS.Resource.COMPLETE:
counter = 0 counter = 0
while os.path.isfile(saved_filename): while os.path.isfile(saved_filename):
+310
View File
@@ -0,0 +1,310 @@
##########################################################
# This RNS example demonstrates how to set up a link to #
# a destination, and identify the initiator to it's peer #
##########################################################
import os
import sys
import time
import argparse
import RNS
# Let's define an app name. We'll use this for all
# destinations we create. Since this echo example
# is part of a range of example utilities, we'll put
# them all within the app namespace "example_utilities"
APP_NAME = "example_utilities"
##########################################################
#### Server Part #########################################
##########################################################
# A reference to the latest client link that connected
latest_client_link = None
# This initialisation is executed when the users chooses
# to run as a server
def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our link example
server_identity = RNS.Identity()
# We create a destination that clients can connect to. We
# want clients to create links to this destination, so we
# need to create a "single" destination type.
server_destination = RNS.Destination(
server_identity,
RNS.Destination.IN,
RNS.Destination.SINGLE,
APP_NAME,
"identifyexample"
)
# We configure a function that will get called every time
# a new client creates a link to this destination.
server_destination.set_link_established_callback(client_connected)
# Everything's ready!
# Let's Wait for client requests or user input
server_loop(server_destination)
def server_loop(destination):
# Let the user know that everything is ready
RNS.log(
"Link identification example "+
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
)
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
# We enter a loop that runs until the users exits.
# If the user hits enter, we will announce our server
# destination on the network, which will let clients
# know how to create messages directed towards it.
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
# When a client establishes a link to our server
# destination, this function will be called with
# a reference to the link.
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)
link.set_remote_identified_callback(remote_identified)
latest_client_link = link
def client_disconnected(link):
RNS.log("Client disconnected")
def remote_identified(identity):
RNS.log("Remote identified as: "+str(identity))
def server_packet_received(message, packet):
global latest_client_link
# Get the originating identity for display
remote_peer = "unidentified peer"
if packet.link.get_remote_identity() != None:
remote_peer = str(packet.link.get_remote_identity())
# 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 from "+remote_peer+": "+text)
reply_text = "I received \""+text+"\" over the link from "+remote_peer
reply_data = reply_text.encode("utf-8")
RNS.Packet(latest_client_link, reply_data).send()
##########################################################
#### Client Part #########################################
##########################################################
# A reference to the server link
server_link = None
# A reference to the client identity
client_identity = None
# This initialisation is executed when the users chooses
# to run as a client
def client(destination_hexhash, configpath):
global client_identity
# We need a binary representation of the destination
# hash that was entered on the command line
try:
if len(destination_hexhash) != 20:
raise ValueError("Destination length is invalid, must be 20 hexadecimal characters (10 bytes)")
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
exit()
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Create a new client identity
client_identity = RNS.Identity()
RNS.log(
"Client created new identity "+
str(client_identity)
)
# Check if we know a path to the destination
if not RNS.Transport.has_path(destination_hash):
RNS.log("Destination is not yet known. Requesting path and waiting for announce to arrive...")
RNS.Transport.request_path(destination_hash)
while not RNS.Transport.has_path(destination_hash):
time.sleep(0.1)
# Recall the server identity
server_identity = RNS.Identity.recall(destination_hash)
# Inform the user that we'll begin connecting
RNS.log("Establishing link with server...")
# When the server identity is known, we set
# up a destination
server_destination = RNS.Destination(
server_identity,
RNS.Destination.OUT,
RNS.Destination.SINGLE,
APP_NAME,
"identifyexample"
)
# And create a link
link = RNS.Link(server_destination)
# We set a callback that will get executed
# every time a packet is received over the
# link
link.set_packet_callback(client_packet_received)
# We'll also set up functions to inform the
# user when the link is established or closed
link.set_link_established_callback(link_established)
link.set_link_closed_callback(link_closed)
# Everything is set up, so let's enter a loop
# for the user to interact with the example
client_loop()
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()
# This function is called when a link
# has been established with the server
def link_established(link):
# We store a reference to the link
# instance for later use
global server_link, client_identity
server_link = link
# Inform the user that the server is
# connected
RNS.log("Link established with server, identifying to remote peer...")
link.identify(client_identity)
# When a link is closed, we'll inform the
# user, and exit the program
def link_closed(link):
if link.teardown_reason == RNS.Link.TIMEOUT:
RNS.log("The link timed out, exiting now")
elif link.teardown_reason == RNS.Link.DESTINATION_CLOSED:
RNS.log("The link was closed by the server, exiting now")
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
# When a packet is received over the link, we
# simply print out the data.
def client_packet_received(message, packet):
text = message.decode("utf-8")
RNS.log("Received data on the link: "+text)
print("> ", end=" ")
sys.stdout.flush()
##########################################################
#### Program Startup #####################################
##########################################################
# This part of the program runs at startup,
# and parses input of from the user, and then
# starts up the desired program mode.
if __name__ == "__main__":
try:
parser = argparse.ArgumentParser(description="Simple link example")
parser.add_argument(
"-s",
"--server",
action="store_true",
help="wait for incoming link requests from clients"
)
parser.add_argument(
"--config",
action="store",
default=None,
help="path to alternative Reticulum config directory",
type=str
)
parser.add_argument(
"destination",
nargs="?",
default=None,
help="hexadecimal hash of the server destination",
type=str
)
args = parser.parse_args()
if args.config:
configarg = args.config
else:
configarg = None
if args.server:
server(configarg)
else:
if (args.destination == None):
print("")
parser.print_help()
print("")
else:
client(args.destination, configarg)
except KeyboardInterrupt:
print("")
exit()
+16 -6
View File
@@ -44,7 +44,7 @@ def server(configpath):
# We configure a function that will get called every time # We configure a function that will get called every time
# a new client creates a link to this destination. # a new client creates a link to this destination.
server_destination.link_established_callback(client_connected) server_destination.set_link_established_callback(client_connected)
# Everything's ready! # Everything's ready!
# Let's Wait for client requests or user input # Let's Wait for client requests or user input
@@ -76,8 +76,8 @@ def client_connected(link):
global latest_client_link global latest_client_link
RNS.log("Client connected") RNS.log("Client connected")
link.link_closed_callback(client_disconnected) link.set_link_closed_callback(client_disconnected)
link.packet_callback(server_packet_received) link.set_packet_callback(server_packet_received)
latest_client_link = link latest_client_link = link
def client_disconnected(link): def client_disconnected(link):
@@ -149,12 +149,12 @@ def client(destination_hexhash, configpath):
# We set a callback that will get executed # We set a callback that will get executed
# every time a packet is received over the # every time a packet is received over the
# link # link
link.packet_callback(client_packet_received) link.set_packet_callback(client_packet_received)
# We'll also set up functions to inform the # We'll also set up functions to inform the
# user when the link is established or closed # user when the link is established or closed
link.link_established_callback(link_established) link.set_link_established_callback(link_established)
link.link_closed_callback(link_closed) link.set_link_closed_callback(link_closed)
# Everything is set up, so let's enter a loop # Everything is set up, so let's enter a loop
# for the user to interact with the example # for the user to interact with the example
@@ -181,8 +181,18 @@ def client_loop():
# If not, send the entered text over the link # If not, send the entered text over the link
if text != "": if text != "":
data = text.encode("utf-8") data = text.encode("utf-8")
if len(data) <= RNS.Link.MDU:
RNS.Packet(server_link, data).send() 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: except Exception as e:
RNS.log("Error while sending data over the link: "+str(e))
should_quit = True should_quit = True
server_link.teardown() server_link.teardown()
+283
View File
@@ -0,0 +1,283 @@
##########################################################
# This RNS example demonstrates how to set perform #
# requests and receive responses over a link. #
##########################################################
import os
import sys
import time
import random
import argparse
import RNS
# Let's define an app name. We'll use this for all
# destinations we create. Since this echo example
# is part of a range of example utilities, we'll put
# them all within the app namespace "example_utilities"
APP_NAME = "example_utilities"
##########################################################
#### Server Part #########################################
##########################################################
# A reference to the latest client link that connected
latest_client_link = None
def random_text_generator(path, data, request_id, remote_identity, requested_at):
RNS.log("Generating response to request "+RNS.prettyhexrep(request_id))
texts = ["They looked up", "On each full moon", "Becky was upset", "Ill stay away from it", "The pet shop stocks everything"]
return texts[random.randint(0, len(texts)-1)]
# This initialisation is executed when the users chooses
# to run as a server
def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our link example
server_identity = RNS.Identity()
# We create a destination that clients can connect to. We
# want clients to create links to this destination, so we
# need to create a "single" destination type.
server_destination = RNS.Destination(
server_identity,
RNS.Destination.IN,
RNS.Destination.SINGLE,
APP_NAME,
"requestexample"
)
# We configure a function that will get called every time
# a new client creates a link to this destination.
server_destination.set_link_established_callback(client_connected)
# We register a request handler for handling incoming
# requests over any established links.
server_destination.register_request_handler(
"/random/text",
response_generator = random_text_generator,
allow = RNS.Destination.ALLOW_ALL
)
# Everything's ready!
# Let's Wait for client requests or user input
server_loop(server_destination)
def server_loop(destination):
# Let the user know that everything is ready
RNS.log(
"Request example "+
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
)
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
# We enter a loop that runs until the users exits.
# If the user hits enter, we will announce our server
# destination on the network, which will let clients
# know how to create messages directed towards it.
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
# When a client establishes a link to our server
# destination, this function will be called with
# a reference to the link.
def client_connected(link):
global latest_client_link
RNS.log("Client connected")
link.set_link_closed_callback(client_disconnected)
latest_client_link = link
def client_disconnected(link):
RNS.log("Client disconnected")
##########################################################
#### Client Part #########################################
##########################################################
# A reference to the server link
server_link = None
# This initialisation is executed when the users chooses
# to run as a client
def client(destination_hexhash, configpath):
# We need a binary representation of the destination
# hash that was entered on the command line
try:
if len(destination_hexhash) != 20:
raise ValueError("Destination length is invalid, must be 20 hexadecimal characters (10 bytes)")
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
exit()
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Check if we know a path to the destination
if not RNS.Transport.has_path(destination_hash):
RNS.log("Destination is not yet known. Requesting path and waiting for announce to arrive...")
RNS.Transport.request_path(destination_hash)
while not RNS.Transport.has_path(destination_hash):
time.sleep(0.1)
# Recall the server identity
server_identity = RNS.Identity.recall(destination_hash)
# Inform the user that we'll begin connecting
RNS.log("Establishing link with server...")
# When the server identity is known, we set
# up a destination
server_destination = RNS.Destination(
server_identity,
RNS.Destination.OUT,
RNS.Destination.SINGLE,
APP_NAME,
"requestexample"
)
# And create a link
link = RNS.Link(server_destination)
# We'll set up functions to inform the
# user when the link is established or closed
link.set_link_established_callback(link_established)
link.set_link_closed_callback(link_closed)
# Everything is set up, so let's enter a loop
# for the user to interact with the example
client_loop()
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()
else:
server_link.request(
"/random/text",
data = None,
response_callback = got_response,
failed_callback = request_failed
)
except Exception as e:
RNS.log("Error while sending request over the link: "+str(e))
should_quit = True
server_link.teardown()
def got_response(request_receipt):
request_id = request_receipt.request_id
response = request_receipt.response
RNS.log("Got response for request "+RNS.prettyhexrep(request_id)+": "+str(response))
def request_received(request_receipt):
RNS.log("The request "+RNS.prettyhexrep(request_receipt.request_id)+" was received by the remote peer.")
def request_failed(request_receipt):
RNS.log("The request "+RNS.prettyhexrep(request_receipt.request_id)+" failed.")
# This function is called when a link
# has been established with the server
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, hit enter to perform a request, or type in \"quit\" to quit")
# When a link is closed, we'll inform the
# user, and exit the program
def link_closed(link):
if link.teardown_reason == RNS.Link.TIMEOUT:
RNS.log("The link timed out, exiting now")
elif link.teardown_reason == RNS.Link.DESTINATION_CLOSED:
RNS.log("The link was closed by the server, exiting now")
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
##########################################################
#### Program Startup #####################################
##########################################################
# This part of the program runs at startup,
# and parses input of from the user, and then
# starts up the desired program mode.
if __name__ == "__main__":
try:
parser = argparse.ArgumentParser(description="Simple request/response example")
parser.add_argument(
"-s",
"--server",
action="store_true",
help="wait for incoming requests from clients"
)
parser.add_argument(
"--config",
action="store",
default=None,
help="path to alternative Reticulum config directory",
type=str
)
parser.add_argument(
"destination",
nargs="?",
default=None,
help="hexadecimal hash of the server destination",
type=str
)
args = parser.parse_args()
if args.config:
configarg = args.config
else:
configarg = None
if args.server:
server(configarg)
else:
if (args.destination == None):
print("")
parser.print_help()
print("")
else:
client(args.destination, configarg)
except KeyboardInterrupt:
print("")
exit()
-54
View File
@@ -1,54 +0,0 @@
Reticulum Wire Format
Header Types
-----------------
type 1 00 Two byte header, one 10 byte address field
type 2 01 Two byte header, two 10 byte address fields
type 3 10 Reserved
type 4 11 Reserved
Propagation Types
-----------------
broadcast 00
transport 01
reserved 10
reserved 11
Destination Types
-----------------
single 00
group 01
plain 10
link 11
Packet Types
-----------------
data 00
announce 01
link request 10
proof 11
+- Packet Example -+
01010000 00000100 [ADDR1, 10 bytes] [ADDR2, 10 bytes] [CONTEXT, 1 byte] [DATA]
| | | | |
| | | | +-- Hops = 4
| | | +------- DATA packet
| | +--------- SINGLE destination
| +----------- TRANSPORT propagation type
+------------- HEADER_2, two byte header, two address fields
+- Packet Example -+
00000000 00000111 [ADDR1, 10 bytes] [CONTEXT, 1 byte] [DATA]
| | | | |
| | | | +-- Hops = 7
| | | +------- DATA packet
| | +--------- SINGLE destination
| +----------- BROADCAST propagation type
+------------- HEADER_1, two byte header, one address field
+12 -41
View File
@@ -18,12 +18,13 @@ For more info, see [unsigned.io/projects/reticulum](https://unsigned.io/projects
## Notable Features ## Notable Features
- Coordination-less globally unique adressing and identification - Coordination-less globally unique adressing and identification
- Fully self-configuring multi-hop routing - Fully self-configuring multi-hop routing
- Asymmetric RSA encryption and signatures as basis for all communication - Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication
- Perfect Forward Secrecy on links with ephemereal Elliptic Curve Diffie-Hellman keys (on Curve25519) - Forward Secrecy with ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519
- Reticulum uses the [Fernet](https://github.com/fernet/spec/blob/master/Spec.md) specification for encryption on links and to group destinations - Reticulum uses the [Fernet](https://github.com/fernet/spec/blob/master/Spec.md) specification for encryption
- AES-128 in CBC mode with PKCS7 padding - AES-128 in CBC mode with PKCS7 padding
- HMAC using SHA256 for authentication - HMAC using SHA256 for authentication
- IVs are generated through os.urandom() - IVs are generated through os.urandom()
- Keys are ephemeral and derived from an ECDH key exchange on Curve25519
- Unforgeable packet delivery confirmations - Unforgeable packet delivery confirmations
- A variety of supported interface types - A variety of supported interface types
- An intuitive and easy-to-use API - An intuitive and easy-to-use API
@@ -31,9 +32,13 @@ For more info, see [unsigned.io/projects/reticulum](https://unsigned.io/projects
- Reticulum can handle a few bytes of data or files of many gigabytes - Reticulum can handle a few bytes of data or files of many gigabytes
- Sequencing, transfer coordination and checksumming is automatic - Sequencing, transfer coordination and checksumming is automatic
- The API is very easy to use, and provides transfer progress - The API is very easy to use, and provides transfer progress
- Lightweight, flexible and expandable Request/Response mechanism
- Efficient link establishment
- Total bandwidth cost of setting up a link is 3 packets totalling 240 bytes
- Low cost of keeping links open at only 0.62 bits per second
## Where can Reticulum be used? ## Where can Reticulum be used?
On practically any hardware that can support at least a half-duplex channel with 1.000 bits per second throughput, and an MTU of 500 bytes. Data radios, modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes, ad-hoc WiFi, free-space optical links and similar systems are all examples of the types of interfaces Reticulum was designed for. Over practically any medium that can support at least a half-duplex channel with 1.000 bits per second throughput, and an MTU of 500 bytes. Data radios, modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes, ad-hoc WiFi, free-space optical links and similar systems are all examples of the types of interfaces Reticulum was designed for.
An open-source LoRa-based interface called [RNode](https://unsigned.io/projects/rnode/) has been designed specifically for use with Reticulum. It is possible to build yourself, or it can be purchased as a complete transceiver that just needs a USB connection to the host. An open-source LoRa-based interface called [RNode](https://unsigned.io/projects/rnode/) has been designed specifically for use with Reticulum. It is possible to build yourself, or it can be purchased as a complete transceiver that just needs a USB connection to the host.
@@ -70,49 +75,15 @@ Some countries still ban the use of encryption when operating under an amateur r
- pyserial - pyserial
## How do I get started? ## How do I get started?
Full documentation and tutorials are coming with the stable alpha release. Until then, you are mostly on your own. If you want to experiment already, you could take a look in the "Examples" folder, for some well-documented example programs. The default configuration file created by Reticulum on the first run is also worth reading. Be sure to also read the [Reticulum Overview Document](http://unsigned.io/wp-content/uploads/2018/04/Reticulum_Overview_v0.4.pdf). The best way to get started with the Reticulum Network Stack depends on what
you want to do. For full details and examples, have a look at the [Getting Started Fast](https://markqvist.github.io/Reticulum/manual/gettingstartedfast.html) section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
If you just need Reticulum as a dependency for another application, the easiest way is probably via pip: If you just need Reticulum as a dependency for another application, the easiest way is via pip:
```bash ```bash
pip3 install rns pip3 install rns
``` ```
For Reticulum development, you might want to get the latest source from GitHub. In that case, don't use pip, but try this recipe:
```bash
# Install dependencies
pip3 install cryptography pyserial
# Clone repository
git clone https://github.com/markqvist/Reticulum.git
# Move into Reticulum folder and symlink library to examples folder
cd Reticulum
ln -s ../RNS ./Examples/
# Run an example
python3 Examples/Echo.py -s
# Unless you've manually created a config file, Reticulum will do so now,
# and immediately exit. Make any necessary changes to the file:
nano ~/.reticulum/config
# ... and launch the example again.
python3 Examples/Echo.py -s
# You can now repeat the process on another computer,
# and run the same example with -h to get command line options.
python3 Examples/Echo.py -h
# Run the example in client mode to "ping" the server.
# Replace the hash below with the actual destination hash of your server.
python3 Examples/Echo.py 3e12fc71692f8ec47bc5
# Have a look at another example
python3 Examples/Filetransfer.py -h
```
The default config file contains examples for using Reticulum with LoRa transceivers (specifically [RNode](https://unsigned.io/projects/rnode/)), packet radio TNCs/modems and UDP. By default a UDP interface is already enabled in the default config, which will enable Reticulum communication in your local ethernet broadcast domain. The default config file contains examples for using Reticulum with LoRa transceivers (specifically [RNode](https://unsigned.io/projects/rnode/)), packet radio TNCs/modems and UDP. By default a UDP interface is already enabled in the default config, which will enable Reticulum communication in your local ethernet broadcast domain.
You can use the examples in the config file to expand communication over other mediums such as packet radio or LoRa, or over fast IP links using the UDP interface. I'll add in-depth tutorials and explanations on these topics later. For now, the included examples will hopefully be enough to get started. You can use the examples in the config file to expand communication over other mediums such as packet radio or LoRa, or over fast IP links using the UDP interface. I'll add in-depth tutorials and explanations on these topics later. For now, the included examples will hopefully be enough to get started.
+58 -21
View File
@@ -5,9 +5,6 @@ import RNS
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric import padding
class Callbacks: class Callbacks:
def __init__(self): def __init__(self):
@@ -31,9 +28,6 @@ class Destination:
:param \*aspects: Any non-zero number of string arguments. :param \*aspects: Any non-zero number of string arguments.
""" """
KEYSIZE = RNS.Identity.KEYSIZE;
PADDINGSIZE= RNS.Identity.PADDINGSIZE;
# Constants # Constants
SINGLE = 0x00 SINGLE = 0x00
GROUP = 0x01 GROUP = 0x01
@@ -46,6 +40,11 @@ class Destination:
PROVE_ALL = 0x23 PROVE_ALL = 0x23
proof_strategies = [PROVE_NONE, PROVE_APP, PROVE_ALL] proof_strategies = [PROVE_NONE, PROVE_APP, PROVE_ALL]
ALLOW_NONE = 0x00
ALLOW_ALL = 0x01
ALLOW_LIST = 0x02
request_policies = [ALLOW_NONE, ALLOW_ALL, ALLOW_LIST]
IN = 0x11; IN = 0x11;
OUT = 0x12; OUT = 0x12;
directions = [IN, OUT] directions = [IN, OUT]
@@ -103,6 +102,7 @@ class Destination:
if not type in Destination.types: raise ValueError("Unknown destination type") if not type in Destination.types: raise ValueError("Unknown destination type")
if not direction in Destination.directions: raise ValueError("Unknown destination direction") if not direction in Destination.directions: raise ValueError("Unknown destination direction")
self.callbacks = Callbacks() self.callbacks = Callbacks()
self.request_handlers = {}
self.type = type self.type = type
self.direction = direction self.direction = direction
self.proof_strategy = Destination.PROVE_NONE self.proof_strategy = Destination.PROVE_NONE
@@ -139,8 +139,8 @@ class Destination:
def announce(self, app_data=None, path_response=False): def announce(self, app_data=None, path_response=False):
""" """
Creates an announce packet for this destination and broadcasts it on Creates an announce packet for this destination and broadcasts it on all
all interfaces. Application specific data can be added to the announce. relevant interfaces. Application specific data can be added to the announce.
:param app_data: *bytes* containing the app_data. :param app_data: *bytes* containing the app_data.
:param path_response: Internal flag used by :ref:`RNS.Transport<api-transport>`. Ignore. :param path_response: Internal flag used by :ref:`RNS.Transport<api-transport>`. Ignore.
@@ -162,10 +162,7 @@ class Destination:
signature = self.identity.sign(signed_data) signature = self.identity.sign(signed_data)
# TODO: Check if this could be optimised by only announce_data = self.identity.get_public_key()+random_hash+signature
# carrying the hash in the destination field, not
# also redundantly inside the signed blob as here
announce_data = self.hash+self.identity.get_public_key()+random_hash+signature
if app_data != None: if app_data != None:
announce_data += app_data announce_data += app_data
@@ -178,7 +175,7 @@ class Destination:
RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context = announce_context).send() RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context = announce_context).send()
def link_established_callback(self, callback): def set_link_established_callback(self, callback):
""" """
Registers a function to be called when a link has been established to Registers a function to be called when a link has been established to
this destination. this destination.
@@ -187,7 +184,7 @@ class Destination:
""" """
self.callbacks.link_established = callback self.callbacks.link_established = callback
def packet_callback(self, callback): def set_packet_callback(self, callback):
""" """
Registers a function to be called when a packet has been received by Registers a function to be called when a packet has been received by
this destination. this destination.
@@ -196,7 +193,7 @@ class Destination:
""" """
self.callbacks.packet = callback self.callbacks.packet = callback
def proof_requested_callback(self, callback): def set_proof_requested_callback(self, callback):
""" """
Registers a function to be called when a proof has been requested for Registers a function to be called when a proof has been requested for
a packet sent to this destination. Allows control over when and if a packet sent to this destination. Allows control over when and if
@@ -217,12 +214,52 @@ class Destination:
else: else:
self.proof_strategy = proof_strategy self.proof_strategy = proof_strategy
def register_request_handler(self, path, response_generator = None, allow = ALLOW_NONE, allowed_list = None):
"""
Registers a request handler.
:param path: The path for the request handler to be registered.
:param response_generator: A function or method with the signature *response_generator(path, data, request_id, remote_identity, requested_at)* to be called. Whatever this funcion returns will be sent as a response to the requester. If the function returns ``None``, no response will be sent.
:param allow: One of ``RNS.Destination.ALLOW_NONE``, ``RNS.Destination.ALLOW_ALL`` or ``RNS.Destination.ALLOW_LIST``. If ``RNS.Destination.ALLOW_LIST`` is set, the request handler will only respond to requests for identified peers in the supplied list.
:param allowed_list: A list of *bytes-like* :ref:`RNS.Identity<api-identity>` hashes.
:raises: ``ValueError`` if any of the supplied arguments are invalid.
"""
if path == None or path == "":
raise ValueError("Invalid path specified")
elif not callable(response_generator):
raise ValueError("Invalid response generator specified")
elif not allow in Destination.request_policies:
raise ValueError("Invalid request policy")
else:
path_hash = RNS.Identity.truncated_hash(path.encode("utf-8"))
request_handler = [path, response_generator, allow, allowed_list]
self.request_handlers[path_hash] = request_handler
def deregister_request_handler(self, path):
"""
Deregisters a request handler.
:param path: The path for the request handler to be deregistered.
:returns: True if the handler was deregistered, otherwise False.
"""
path_hash = RNS.Identity.truncated_hash(path.encode("utf-8"))
if path_hash in self.request_handlers:
self.request_handlers.pop(path_hash)
return True
else:
return False
def receive(self, packet): def receive(self, packet):
if packet.packet_type == RNS.Packet.LINKREQUEST:
plaintext = packet.data
self.incoming_link_request(plaintext, packet)
else:
plaintext = self.decrypt(packet.data) plaintext = self.decrypt(packet.data)
if plaintext != None: if plaintext != None:
if packet.packet_type == RNS.Packet.LINKREQUEST:
self.incoming_link_request(plaintext, packet)
if packet.packet_type == RNS.Packet.DATA: if packet.packet_type == RNS.Packet.DATA:
if self.callbacks.packet != None: if self.callbacks.packet != None:
self.callbacks.packet(plaintext, packet) self.callbacks.packet(plaintext, packet)
@@ -245,8 +282,8 @@ class Destination:
raise TypeError("A single destination holds keys through an Identity instance") raise TypeError("A single destination holds keys through an Identity instance")
if self.type == Destination.GROUP: if self.type == Destination.GROUP:
self.prv_bytes = Fernet.generate_key() self.prv_bytes = base64.urlsafe_b64decode(Fernet.generate_key())
self.prv = Fernet(self.prv_bytes) self.prv = Fernet(base64.urlsafe_b64encode(self.prv_bytes))
def get_private_key(self): def get_private_key(self):
@@ -278,7 +315,7 @@ class Destination:
if self.type == Destination.GROUP: if self.type == Destination.GROUP:
self.prv_bytes = key self.prv_bytes = key
self.prv = Fernet(self.prv_bytes) self.prv = Fernet(base64.urlsafe_b64encode(self.prv_bytes))
def load_public_key(self, key): def load_public_key(self, key):
if self.type != Destination.SINGLE: if self.type != Destination.SINGLE:
+157 -113
View File
@@ -4,14 +4,15 @@ import os
import RNS import RNS
import time import time
import atexit import atexit
import base64
from .vendor import umsgpack as umsgpack from .vendor import umsgpack as umsgpack
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.serialization import load_der_public_key from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
from cryptography.hazmat.primitives.serialization import load_der_private_key from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives.asymmetric import padding from cryptography.fernet import Fernet
class Identity: class Identity:
""" """
@@ -19,26 +20,29 @@ class Identity:
for encryption, decryption, signatures and verification, and is the basis for encryption, decryption, signatures and verification, and is the basis
for all encrypted communication over Reticulum networks. for all encrypted communication over Reticulum networks.
:param public_only: Specifies whether this destination only holds a public key. :param create_keys: Specifies whether new encryption and signing keys should be generated.
""" """
KEYSIZE = 1024
CURVE = "Curve25519"
""" """
RSA key size in bits. The curve used for Elliptic Curve DH key exchanges
"""
KEYSIZE = 256*2
"""
X25519 key size in bits. A complete key is the concatenation of a 256 bit encryption key, and a 256 bit signing key.
""" """
DERKEYSIZE = KEYSIZE+272
# Non-configurable constants # Non-configurable constants
PADDINGSIZE = 336 # In bits AES_HMAC_OVERHEAD = 58 # In bytes
AES128_BLOCKSIZE = 16 # In bytes
HASHLENGTH = 256 # In bits HASHLENGTH = 256 # In bits
SIGLENGTH = KEYSIZE SIGLENGTH = KEYSIZE # In bits
ENCRYPT_CHUNKSIZE = (KEYSIZE-PADDINGSIZE)//8
DECRYPT_CHUNKSIZE = KEYSIZE//8
TRUNCATED_HASHLENGTH = 80 # In bits TRUNCATED_HASHLENGTH = 80 # In bits
""" """
Constant specifying the truncated hash length (in bits) used by Reticulum Constant specifying the truncated hash length (in bits) used by Reticulum
for addressable hashes. Non-configurable. for addressable hashes and other purposes. Non-configurable.
""" """
# Storage # Storage
@@ -46,6 +50,9 @@ class Identity:
@staticmethod @staticmethod
def remember(packet_hash, destination_hash, public_key, app_data = None): def remember(packet_hash, destination_hash, public_key, app_data = None):
if len(public_key) != Identity.KEYSIZE//8:
raise TypeError("Can't remember "+RNS.prettyhexrep(destination_hash)+", the public key size of "+str(len(public_key))+" is not valid.", RNS.LOG_ERROR)
else:
Identity.known_destinations[destination_hash] = [time.time(), packet_hash, public_key, app_data] Identity.known_destinations[destination_hash] = [time.time(), packet_hash, public_key, app_data]
@@ -60,7 +67,7 @@ class Identity:
RNS.log("Searching for "+RNS.prettyhexrep(destination_hash)+"...", RNS.LOG_EXTREME) RNS.log("Searching for "+RNS.prettyhexrep(destination_hash)+"...", RNS.LOG_EXTREME)
if destination_hash in Identity.known_destinations: if destination_hash in Identity.known_destinations:
identity_data = Identity.known_destinations[destination_hash] identity_data = Identity.known_destinations[destination_hash]
identity = Identity(public_only=True) identity = Identity(create_keys=False)
identity.load_public_key(identity_data[2]) identity.load_public_key(identity_data[2])
identity.app_data = identity_data[3] identity.app_data = identity_data[3]
RNS.log("Found "+RNS.prettyhexrep(destination_hash)+" in known destinations", RNS.LOG_EXTREME) RNS.log("Found "+RNS.prettyhexrep(destination_hash)+" in known destinations", RNS.LOG_EXTREME)
@@ -145,19 +152,19 @@ class Identity:
if packet.packet_type == RNS.Packet.ANNOUNCE: if packet.packet_type == RNS.Packet.ANNOUNCE:
RNS.log("Validating announce from "+RNS.prettyhexrep(packet.destination_hash), RNS.LOG_DEBUG) RNS.log("Validating announce from "+RNS.prettyhexrep(packet.destination_hash), RNS.LOG_DEBUG)
destination_hash = packet.destination_hash destination_hash = packet.destination_hash
public_key = packet.data[10:Identity.DERKEYSIZE//8+10] public_key = packet.data[:Identity.KEYSIZE//8]
random_hash = packet.data[Identity.DERKEYSIZE//8+10:Identity.DERKEYSIZE//8+20] random_hash = packet.data[Identity.KEYSIZE//8:Identity.KEYSIZE//8+10]
signature = packet.data[Identity.DERKEYSIZE//8+20:Identity.DERKEYSIZE//8+20+Identity.KEYSIZE//8] signature = packet.data[Identity.KEYSIZE//8+10:Identity.KEYSIZE//8+10+Identity.KEYSIZE//8]
app_data = b"" app_data = b""
if len(packet.data) > Identity.DERKEYSIZE//8+20+Identity.KEYSIZE//8: if len(packet.data) > Identity.KEYSIZE//8+10+Identity.KEYSIZE//8:
app_data = packet.data[Identity.DERKEYSIZE//8+20+Identity.KEYSIZE//8:] app_data = packet.data[Identity.KEYSIZE//8+10+Identity.KEYSIZE//8:]
signed_data = destination_hash+public_key+random_hash+app_data signed_data = destination_hash+public_key+random_hash+app_data
if not len(packet.data) > Identity.DERKEYSIZE//8+20+Identity.KEYSIZE//8: if not len(packet.data) > Identity.KEYSIZE//8+10+Identity.KEYSIZE//8:
app_data = None app_data = None
announced_identity = Identity(public_only=True) announced_identity = Identity(create_keys=False)
announced_identity.load_public_key(public_key) announced_identity.load_public_key(public_key)
if announced_identity.pub != None and announced_identity.validate(signature, signed_data): if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
@@ -184,40 +191,71 @@ class Identity:
:param path: The full path to the saved :ref:`RNS.Identity<api-identity>` data :param path: The full path to the saved :ref:`RNS.Identity<api-identity>` data
:returns: A :ref:`RNS.Identity<api-identity>` instance, or *None* if the loaded data was invalid. :returns: A :ref:`RNS.Identity<api-identity>` instance, or *None* if the loaded data was invalid.
""" """
identity = Identity(public_only=True) identity = Identity(create_keys=False)
if identity.load(path): if identity.load(path):
return identity return identity
else: else:
return None return None
@staticmethod
def from_bytes(prv_bytes):
"""
Create a new :ref:`RNS.Identity<api-identity>` instance from *bytes* of private key.
Can be used to load previously created and saved identities into Reticulum.
def __init__(self,public_only=False): :param prv_bytes: The *bytes* of private a saved private key. **HAZARD!** Never not use this to generate a new key by feeding random data in prv_bytes.
:returns: A :ref:`RNS.Identity<api-identity>` instance, or *None* if the *bytes* data was invalid.
"""
identity = Identity(create_keys=False)
if identity.load_private_key(prv_bytes):
return identity
else:
return None
def __init__(self,create_keys=True):
# Initialize keys to none # Initialize keys to none
self.prv = None self.prv = None
self.pub = None
self.prv_bytes = None self.prv_bytes = None
self.sig_prv = None
self.sig_prv_bytes = None
self.pub = None
self.pub_bytes = None self.pub_bytes = None
self.sig_pub = None
self.sig_pub_bytes = None
self.hash = None self.hash = None
self.hexhash = None self.hexhash = None
if not public_only: if create_keys:
self.create_keys() self.create_keys()
def create_keys(self): def create_keys(self):
self.prv = rsa.generate_private_key( self.prv = X25519PrivateKey.generate()
public_exponent=65537,
key_size=Identity.KEYSIZE,
backend=default_backend()
)
self.prv_bytes = self.prv.private_bytes( self.prv_bytes = self.prv.private_bytes(
encoding=serialization.Encoding.DER, encoding=serialization.Encoding.Raw,
format=serialization.PrivateFormat.PKCS8, format=serialization.PrivateFormat.Raw,
encryption_algorithm=serialization.NoEncryption() 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 = self.prv.public_key()
self.pub_bytes = self.pub.public_bytes( self.pub_bytes = self.pub.public_bytes(
encoding=serialization.Encoding.DER, encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.SubjectPublicKeyInfo 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() self.update_hashes()
@@ -228,13 +266,13 @@ class Identity:
""" """
:returns: The private key as *bytes* :returns: The private key as *bytes*
""" """
return self.prv_bytes return self.prv_bytes+self.sig_prv_bytes
def get_public_key(self): def get_public_key(self):
""" """
:returns: The public key as *bytes* :returns: The public key as *bytes*
""" """
return self.pub_bytes return self.pub_bytes+self.sig_pub_bytes
def load_private_key(self, prv_bytes): def load_private_key(self, prv_bytes):
""" """
@@ -244,42 +282,53 @@ class Identity:
:returns: True if the key was loaded, otherwise False. :returns: True if the key was loaded, otherwise False.
""" """
try: try:
self.prv_bytes = prv_bytes self.prv_bytes = prv_bytes[:Identity.KEYSIZE//8//2]
self.prv = serialization.load_der_private_key( self.prv = X25519PrivateKey.from_private_bytes(self.prv_bytes)
self.prv_bytes, self.sig_prv_bytes = prv_bytes[Identity.KEYSIZE//8//2:]
password=None, self.sig_prv = Ed25519PrivateKey.from_private_bytes(self.sig_prv_bytes)
backend=default_backend()
)
self.pub = self.prv.public_key() self.pub = self.prv.public_key()
self.pub_bytes = self.pub.public_bytes( self.pub_bytes = self.pub.public_bytes(
encoding=serialization.Encoding.DER, encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.SubjectPublicKeyInfo 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() self.update_hashes()
return True return True
except Exception as e: except Exception as e:
raise e
RNS.log("Failed to load identity key", RNS.LOG_ERROR) RNS.log("Failed to load identity key", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
return False return False
def load_public_key(self, key): def load_public_key(self, pub_bytes):
""" """
Load a public key into the instance. Load a public key into the instance.
:param prv_bytes: The public key as *bytes*. :param pub_bytes: The public key as *bytes*.
:returns: True if the key was loaded, otherwise False. :returns: True if the key was loaded, otherwise False.
""" """
try: try:
self.pub_bytes = key self.pub_bytes = pub_bytes[:Identity.KEYSIZE//8//2]
self.pub = load_der_public_key(self.pub_bytes, backend=default_backend()) self.sig_pub_bytes = pub_bytes[Identity.KEYSIZE//8//2:]
self.pub = X25519PublicKey.from_public_bytes(self.pub_bytes)
self.sig_pub = Ed25519PublicKey.from_public_bytes(self.sig_pub_bytes)
self.update_hashes() self.update_hashes()
except Exception as e: except Exception as e:
RNS.log("Error while loading public key, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("Error while loading public key, the contained exception was: "+str(e), RNS.LOG_ERROR)
def update_hashes(self): def update_hashes(self):
self.hash = Identity.truncated_hash(self.pub_bytes) self.hash = Identity.truncated_hash(self.get_public_key())
self.hexhash = self.hash.hex() self.hexhash = self.hash.hex()
def to_file(self, path): def to_file(self, path):
@@ -293,7 +342,7 @@ class Identity:
""" """
try: try:
with open(path, "wb") as key_file: with open(path, "wb") as key_file:
key_file.write(self.prv_bytes) key_file.write(self.get_public_key())
return True return True
return False return False
except Exception as e: except Exception as e:
@@ -310,71 +359,78 @@ class Identity:
RNS.log("Error while loading identity from "+str(path), RNS.LOG_ERROR) RNS.log("Error while loading identity from "+str(path), RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e)) RNS.log("The contained exception was: "+str(e))
def get_salt(self):
return self.hash
def get_context(self):
return None
def encrypt(self, plaintext): def encrypt(self, plaintext):
""" """
Encrypts information for the identity. Encrypts information for the identity.
:param plaintext: The plaintext to be encrypted as *bytes*. :param plaintext: The plaintext to be encrypted as *bytes*.
:returns: Ciphertext as *bytes*. :returns: Ciphertext token as *bytes*.
:raises: *KeyError* if the instance does not hold a public key :raises: *KeyError* if the instance does not hold a public key.
""" """
if self.pub != None: if self.pub != None:
chunksize = Identity.ENCRYPT_CHUNKSIZE ephemeral_key = X25519PrivateKey.generate()
chunks = int(math.ceil(len(plaintext)/(float(chunksize)))) ephemeral_pub_bytes = ephemeral_key.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
ciphertext = b""; format=serialization.PublicFormat.Raw
for chunk in range(chunks):
start = chunk*chunksize
end = (chunk+1)*chunksize
if (chunk+1)*chunksize > len(plaintext):
end = len(plaintext)
ciphertext += self.pub.encrypt(
plaintext[start:end],
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(),
label=None
) )
)
return ciphertext shared_key = ephemeral_key.exchange(self.pub)
derived_key = derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=self.get_salt(),
info=self.get_context(),
).derive(shared_key)
fernet = Fernet(base64.urlsafe_b64encode(derived_key))
ciphertext = base64.urlsafe_b64decode(fernet.encrypt(plaintext))
token = ephemeral_pub_bytes+ciphertext
return token
else: else:
raise KeyError("Encryption failed because identity does not hold a public key") raise KeyError("Encryption failed because identity does not hold a public key")
def decrypt(self, ciphertext): def decrypt(self, ciphertext_token):
""" """
Decrypts information for the identity. Decrypts information for the identity.
:param ciphertext: The ciphertext to be decrypted as *bytes*. :param ciphertext: The ciphertext to be decrypted as *bytes*.
:returns: Plaintext as *bytes*, or *None* if decryption fails. :returns: Plaintext as *bytes*, or *None* if decryption fails.
:raises: *KeyError* if the instance does not hold a private key :raises: *KeyError* if the instance does not hold a private key.
""" """
if self.prv != None: if self.prv != None:
if len(ciphertext_token) > Identity.KEYSIZE//8//2:
plaintext = None plaintext = None
try: try:
chunksize = Identity.DECRYPT_CHUNKSIZE peer_pub_bytes = ciphertext_token[:Identity.KEYSIZE//8//2]
chunks = int(math.ceil(len(ciphertext)/(float(chunksize)))) peer_pub = X25519PublicKey.from_public_bytes(peer_pub_bytes)
plaintext = b""; shared_key = self.prv.exchange(peer_pub)
for chunk in range(chunks): derived_key = derived_key = HKDF(
start = chunk*chunksize algorithm=hashes.SHA256(),
end = (chunk+1)*chunksize length=32,
if (chunk+1)*chunksize > len(ciphertext): salt=self.get_salt(),
end = len(ciphertext) info=self.get_context(),
).derive(shared_key)
plaintext += self.prv.decrypt( fernet = Fernet(base64.urlsafe_b64encode(derived_key))
ciphertext[start:end], ciphertext = ciphertext_token[Identity.KEYSIZE//8//2:]
padding.OAEP( plaintext = fernet.decrypt(base64.urlsafe_b64encode(ciphertext))
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(), except Exception as e:
label=None RNS.log("Decryption by "+RNS.prettyhexrep(self.hash)+" failed: "+str(e), RNS.LOG_DEBUG)
)
)
except:
RNS.log("Decryption by "+RNS.prettyhexrep(self.hash)+" failed", RNS.LOG_VERBOSE)
return plaintext; return plaintext;
else:
RNS.log("Decryption failed because the token size was invalid.", RNS.LOG_DEBUG)
return None
else: else:
raise KeyError("Decryption failed because identity does not hold a private key") raise KeyError("Decryption failed because identity does not hold a private key")
@@ -385,18 +441,14 @@ class Identity:
:param message: The message to be signed as *bytes*. :param message: The message to be signed as *bytes*.
:returns: Signature as *bytes*. :returns: Signature as *bytes*.
:raises: *KeyError* if the instance does not hold a private key :raises: *KeyError* if the instance does not hold a private key.
""" """
if self.prv != None: if self.sig_prv != None:
signature = self.prv.sign( try:
message, return self.sig_prv.sign(message)
padding.PSS( except Exception as e:
mgf=padding.MGF1(hashes.SHA256()), RNS.log("The identity "+str(self)+" could not sign the requested message. The contained exception was: "+str(e), RNS.LOG_ERROR)
salt_length=padding.PSS.MAX_LENGTH raise e
),
hashes.SHA256()
)
return signature
else: else:
raise KeyError("Signing failed because identity does not hold a private key") raise KeyError("Signing failed because identity does not hold a private key")
@@ -407,19 +459,11 @@ class Identity:
:param signature: The signature to be validated as *bytes*. :param signature: The signature to be validated as *bytes*.
:param message: The message to be validated as *bytes*. :param message: The message to be validated as *bytes*.
:returns: True if the signature is valid, otherwise False. :returns: True if the signature is valid, otherwise False.
:raises: *KeyError* if the instance does not hold a public key :raises: *KeyError* if the instance does not hold a public key.
""" """
if self.pub != None: if self.pub != None:
try: try:
self.pub.verify( self.sig_pub.verify(signature, message)
signature,
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
return True return True
except Exception as e: except Exception as e:
return False return False
+11 -1
View File
@@ -1,6 +1,7 @@
from .Interface import Interface from .Interface import Interface
import socketserver import socketserver
import threading import threading
import netifaces
import socket import socket
import time import time
import sys import sys
@@ -127,12 +128,21 @@ class TCPClientInterface(Interface):
class TCPServerInterface(Interface): class TCPServerInterface(Interface):
@staticmethod
def get_address_for_if(name):
return netifaces.ifaddresses(name)[netifaces.AF_INET][0]['addr']
def __init__(self, owner, name, bindip=None, bindport=None): def get_broadcast_for_if(name):
return netifaces.ifaddresses(name)[netifaces.AF_INET][0]['broadcast']
def __init__(self, owner, name, device=None, bindip=None, bindport=None):
self.IN = True self.IN = True
self.OUT = False self.OUT = False
self.name = name self.name = name
if device != None:
bindip = TCPServerInterface.get_address_for_if(device)
if (bindip != None and bindport != None): if (bindip != None and bindport != None):
self.receives = True self.receives = True
self.bind_ip = bindip self.bind_ip = bindip
+17 -1
View File
@@ -1,18 +1,34 @@
from .Interface import Interface from .Interface import Interface
import socketserver import socketserver
import threading import threading
import netifaces
import socket import socket
import time import time
import sys import sys
import RNS import RNS
class UDPInterface(Interface): class UDPInterface(Interface):
def __init__(self, owner, name, bindip=None, bindport=None, forwardip=None, forwardport=None): @staticmethod
def get_address_for_if(name):
return netifaces.ifaddresses(name)[netifaces.AF_INET][0]['addr']
def get_broadcast_for_if(name):
return netifaces.ifaddresses(name)[netifaces.AF_INET][0]['broadcast']
def __init__(self, owner, name, device=None, bindip=None, bindport=None, forwardip=None, forwardport=None):
self.IN = True self.IN = True
self.OUT = False self.OUT = False
self.name = name self.name = name
if device != None:
if bindip == None:
bindip = UDPInterface.get_broadcast_for_if(device)
if forwardip == None:
forwardip = UDPInterface.get_broadcast_for_if(device)
if (bindip != None and bindport != None): if (bindip != None and bindport != None):
self.receives = True self.receives = True
self.bind_ip = bindip self.bind_ip = bindip
+279 -30
View File
@@ -23,6 +23,7 @@ class LinkCallbacks:
self.resource = None self.resource = None
self.resource_started = None self.resource_started = None
self.resource_concluded = None self.resource_concluded = None
self.remote_identified = None
class Link: class Link:
""" """
@@ -33,17 +34,15 @@ class Link:
:param peer_pub_bytes: Internal use, ignore this argument. :param peer_pub_bytes: Internal use, ignore this argument.
:param peer_sig_pub_bytes: Internal use, ignore this argument. :param peer_sig_pub_bytes: Internal use, ignore this argument.
""" """
CURVE = "Curve25519" CURVE = RNS.Identity.CURVE
""" """
The curve used for Elliptic Curve DH key exchanges The curve used for Elliptic Curve DH key exchanges
""" """
ECPUBSIZE = 32+32 ECPUBSIZE = 32+32
BLOCKSIZE = 16
KEYSIZE = 32 KEYSIZE = 32
AES_HMAC_OVERHEAD = 58 MDU = math.floor((RNS.Reticulum.MDU-RNS.Identity.AES_HMAC_OVERHEAD)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1
MDU = math.floor((RNS.Reticulum.MDU-AES_HMAC_OVERHEAD)/BLOCKSIZE)*BLOCKSIZE - 1
# TODO: This should not be hardcoded, # TODO: This should not be hardcoded,
# but calculated from something like # but calculated from something like
@@ -54,7 +53,7 @@ class Link:
""" """
TIMEOUT_FACTOR = 3 TIMEOUT_FACTOR = 3
STALE_GRACE = 2 STALE_GRACE = 2
KEEPALIVE = 180 KEEPALIVE = 360
""" """
Interval for sending keep-alive packets on established links in seconds. Interval for sending keep-alive packets on established links in seconds.
""" """
@@ -90,11 +89,6 @@ class Link:
link.last_inbound = time.time() link.last_inbound = time.time()
link.start_watchdog() link.start_watchdog()
# TODO: Why was link_established callback here? Seems weird
# to call this before RTT packet has been received
#if self.owner.callbacks.link_established != None:
# self.owner.callbacks.link_established(link)
RNS.log("Incoming link request "+str(link)+" accepted", RNS.LOG_VERBOSE) RNS.log("Incoming link request "+str(link)+" accepted", RNS.LOG_VERBOSE)
return link return link
@@ -116,6 +110,7 @@ class Link:
self.resource_strategy = Link.ACCEPT_NONE self.resource_strategy = Link.ACCEPT_NONE
self.outgoing_resources = [] self.outgoing_resources = []
self.incoming_resources = [] self.incoming_resources = []
self.pending_requests = []
self.last_inbound = 0 self.last_inbound = 0
self.last_outbound = 0 self.last_outbound = 0
self.tx = 0 self.tx = 0
@@ -132,17 +127,19 @@ class Link:
self.owner = owner self.owner = owner
self.destination = destination self.destination = destination
self.attached_interface = None self.attached_interface = None
self.__remote_identity = None
self.__encryption_disabled = False self.__encryption_disabled = False
if self.destination == None: if self.destination == None:
self.initiator = False self.initiator = False
self.prv = self.owner.identity.prv
self.sig_prv = self.owner.identity.sig_prv
else: else:
self.initiator = True self.initiator = True
self.fernet = None
self.prv = X25519PrivateKey.generate() self.prv = X25519PrivateKey.generate()
self.sig_prv = Ed25519PrivateKey.generate() self.sig_prv = Ed25519PrivateKey.generate()
self.fernet = None
self.pub = self.prv.public_key() self.pub = self.prv.public_key()
self.pub_bytes = self.pub.public_bytes( self.pub_bytes = self.pub.public_bytes(
encoding=serialization.Encoding.Raw, encoding=serialization.Encoding.Raw,
@@ -162,10 +159,14 @@ class Link:
self.load_peer(peer_pub_bytes, peer_sig_pub_bytes) self.load_peer(peer_pub_bytes, peer_sig_pub_bytes)
if (self.initiator): if (self.initiator):
peer_pub_bytes = self.destination.identity.get_public_key()[:Link.ECPUBSIZE//2]
peer_sig_pub_bytes = self.destination.identity.get_public_key()[Link.ECPUBSIZE//2:Link.ECPUBSIZE]
self.request_data = self.pub_bytes+self.sig_pub_bytes self.request_data = self.pub_bytes+self.sig_pub_bytes
self.packet = RNS.Packet(destination, self.request_data, packet_type=RNS.Packet.LINKREQUEST) self.packet = RNS.Packet(destination, self.request_data, packet_type=RNS.Packet.LINKREQUEST)
self.packet.pack() self.packet.pack()
self.set_link_id(self.packet) self.set_link_id(self.packet)
self.load_peer(peer_pub_bytes, peer_sig_pub_bytes)
self.handshake()
RNS.Transport.register_link(self) RNS.Transport.register_link(self)
self.request_time = time.time() self.request_time = time.time()
self.start_watchdog() self.start_watchdog()
@@ -202,7 +203,7 @@ class Link:
signed_data = self.link_id+self.pub_bytes+self.sig_pub_bytes signed_data = self.link_id+self.pub_bytes+self.sig_pub_bytes
signature = self.owner.identity.sign(signed_data) signature = self.owner.identity.sign(signed_data)
proof_data = self.pub_bytes+self.sig_pub_bytes+signature proof_data = signature
proof = RNS.Packet(self, proof_data, packet_type=RNS.Packet.PROOF, context=RNS.Packet.LRPROOF) proof = RNS.Packet(self, proof_data, packet_type=RNS.Packet.PROOF, context=RNS.Packet.LRPROOF)
proof.send() proof.send()
self.had_outbound() self.had_outbound()
@@ -221,17 +222,14 @@ class Link:
self.had_outbound() self.had_outbound()
def validate_proof(self, packet): def validate_proof(self, packet):
if self.initiator and len(packet.data) == Link.ECPUBSIZE+RNS.Identity.KEYSIZE//8: if self.initiator and len(packet.data) == RNS.Identity.SIGLENGTH//8:
peer_pub_bytes = packet.data[:Link.ECPUBSIZE//2] signed_data = self.link_id+self.peer_pub_bytes+self.peer_sig_pub_bytes
peer_sig_pub_bytes = packet.data[Link.ECPUBSIZE//2:Link.ECPUBSIZE] signature = packet.data[:RNS.Identity.SIGLENGTH//8]
signed_data = self.link_id+peer_pub_bytes+peer_sig_pub_bytes
signature = packet.data[Link.ECPUBSIZE:RNS.Identity.KEYSIZE//8+Link.ECPUBSIZE]
if self.destination.identity.validate(signature, signed_data): if self.destination.identity.validate(signature, signed_data):
self.load_peer(peer_pub_bytes, peer_sig_pub_bytes)
self.handshake()
self.rtt = time.time() - self.request_time self.rtt = time.time() - self.request_time
self.attached_interface = packet.receiving_interface self.attached_interface = packet.receiving_interface
self.__remote_identity = self.destination.identity
RNS.Transport.activate_link(self) RNS.Transport.activate_link(self)
RNS.log("Link "+str(self)+" established with "+str(self.destination)+", RTT is "+str(self.rtt), RNS.LOG_VERBOSE) 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_data = umsgpack.packb(self.rtt)
@@ -249,6 +247,60 @@ class Link:
RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG) RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG)
def identify(self, identity):
"""
Identifies the initiator of the link to the remote peer. This can only happen
once the link has been established, and is carried out over the encrypted link.
The identity is only revealed to the remote peer, and initiator anonymity is
thus preserved. This method can be used for authentication.
:param identity: An RNS.Identity instance to identify as.
"""
if self.initiator:
signed_data = self.link_id + identity.get_public_key()
signature = identity.sign(signed_data)
proof_data = identity.get_public_key() + signature
proof = RNS.Packet(self, proof_data, RNS.Packet.DATA, context = RNS.Packet.LINKIDENTIFY)
proof.send()
self.had_outbound()
def request(self, path, data = None, response_callback = None, failed_callback = None):
"""
Sends a request to the remote peer.
:param path: The request path.
:param response_callback: A function or method with the signature *response_callback(request_receipt)* to be called when a response is received. See the :ref:`Request Example<example-request>` for more info.
:param failed_callback: A function or method with the signature *failed_callback(request_receipt)* to be called when a request fails. See the :ref:`Request Example<example-request>` for more info.
"""
request_path_hash = RNS.Identity.truncated_hash(path.encode("utf-8"))
unpacked_request = [time.time(), request_path_hash, data]
packed_request = umsgpack.packb(unpacked_request)
if len(packed_request) <= Link.MDU:
request_packet = RNS.Packet(self, packed_request, RNS.Packet.DATA, context = RNS.Packet.REQUEST)
return RequestReceipt(
self,
packet_receipt = request_packet.send(),
response_callback = response_callback,
failed_callback = failed_callback
)
else:
request_id = RNS.Identity.truncated_hash(packed_request)
RNS.log("Sending request "+RNS.prettyhexrep(request_id)+" as resource.", RNS.LOG_DEBUG)
request_resource = RNS.Resource(packed_request, self, request_id = request_id, is_response = False)
return RequestReceipt(
self,
resource = request_resource,
response_callback = response_callback,
failed_callback = failed_callback
)
def rtt_packet(self, packet): def rtt_packet(self, packet):
try: try:
# TODO: This is crude, we should use the delta # TODO: This is crude, we should use the delta
@@ -265,8 +317,7 @@ class Link:
if self.owner.callbacks.link_established != None: if self.owner.callbacks.link_established != None:
self.owner.callbacks.link_established(self) self.owner.callbacks.link_established(self)
except Exception as e: except Exception as e:
RNS.log("Error occurred while processing RTT packet, tearing down link", RNS.LOG_ERROR) RNS.log("Error occurred while processing RTT packet, tearing down link. The contained exception was: "+str(e), RNS.LOG_ERROR)
traceback.print_exc()
self.teardown() self.teardown()
def get_salt(self): def get_salt(self):
@@ -293,6 +344,12 @@ class Link:
""" """
return min(self.no_inbound_for(), self.no_outbound_for()) return min(self.no_inbound_for(), self.no_outbound_for())
def get_remote_identity(self):
"""
:returns: The identity of the remote peer, if it is known
"""
return self.__remote_identity
def had_outbound(self): def had_outbound(self):
self.last_outbound = time.time() self.last_outbound = time.time()
@@ -404,6 +461,77 @@ class Link:
keepalive_packet.send() keepalive_packet.send()
self.had_outbound() self.had_outbound()
def handle_request(self, request_id, unpacked_request):
requested_at = unpacked_request[0]
path_hash = unpacked_request[1]
request_data = unpacked_request[2]
if path_hash in self.destination.request_handlers:
request_handler = self.destination.request_handlers[path_hash]
path = request_handler[0]
response_generator = request_handler[1]
allow = request_handler[2]
allowed_list = request_handler[3]
allowed = False
if not allow == RNS.Destination.ALLOW_NONE:
if allow == RNS.Destination.ALLOW_LIST:
if self.__remote_identity.hash in allowed_list:
allowed = True
elif allow == RNS.Destination.ALLOW_ALL:
allowed = True
if allowed:
RNS.log("Handling request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_DEBUG)
response = response_generator(path, request_data, request_id, self.__remote_identity, requested_at)
if response != None:
packed_response = umsgpack.packb([request_id, response])
if len(packed_response) <= Link.MDU:
RNS.Packet(self, packed_response, RNS.Packet.DATA, context = RNS.Packet.RESPONSE).send()
else:
response_resource = RNS.Resource(packed_response, self, request_id = request_id, is_response = True)
else:
identity_string = RNS.prettyhexrep(self.get_remote_identity()) if self.get_remote_identity() != None else "<Unknown>"
RNS.log("Request "+RNS.prettyhexrep(request_id)+" from "+identity_string+" not allowed for: "+str(path), RNS.LOG_DEBUG)
def handle_response(self, request_id, response_data):
remove = None
for pending_request in self.pending_requests:
if pending_request.request_id == request_id:
remove = pending_request
try:
pending_request.response_received(response_data)
except Exception as e:
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
break
if remove != None:
self.pending_requests.remove(remove)
def request_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE:
packed_request = resource.data.read()
unpacked_request = umsgpack.unpackb(packed_request)
request_id = RNS.Identity.truncated_hash(packed_request)
request_data = unpacked_request
self.handle_request(request_id, request_data)
else:
RNS.log("Incoming request resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
def response_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE:
packed_response = resource.data.read()
unpacked_response = umsgpack.unpackb(packed_response)
request_id = unpacked_response[0]
response_data = unpacked_response[1]
self.handle_response(request_id, response_data)
else:
RNS.log("Incoming response resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
def receive(self, packet): def receive(self, packet):
self.watchdog_lock = True self.watchdog_lock = True
if not self.status == Link.CLOSED and not (self.initiator and packet.context == RNS.Packet.KEEPALIVE and packet.data == bytes([0xFF])): if not self.status == Link.CLOSED and not (self.initiator and packet.context == RNS.Packet.KEEPALIVE and packet.data == bytes([0xFF])):
@@ -431,6 +559,40 @@ class Link:
if self.destination.callbacks.proof_requested: if self.destination.callbacks.proof_requested:
self.destination.callbacks.proof_requested(packet) self.destination.callbacks.proof_requested(packet)
elif packet.context == RNS.Packet.LINKIDENTIFY:
plaintext = self.decrypt(packet.data)
if not self.initiator and len(plaintext) == RNS.Identity.KEYSIZE//8 + RNS.Identity.SIGLENGTH//8:
public_key = plaintext[:RNS.Identity.KEYSIZE//8]
signed_data = self.link_id+public_key
signature = plaintext[RNS.Identity.KEYSIZE//8:RNS.Identity.KEYSIZE//8+RNS.Identity.SIGLENGTH//8]
identity = RNS.Identity(create_keys=False)
identity.load_public_key(public_key)
if identity.validate(signature, signed_data):
self.__remote_identity = identity
if self.callbacks.remote_identified != None:
self.callbacks.remote_identified(self.__remote_identity)
elif packet.context == RNS.Packet.REQUEST:
try:
request_id = packet.getTruncatedHash()
packed_request = self.decrypt(packet.data)
unpacked_request = umsgpack.unpackb(packed_request)
self.handle_request(request_id, unpacked_request)
except Exception as e:
RNS.log("Error occurred while handling request. The contained exception was: "+str(e), RNS.LOG_ERROR)
elif packet.context == RNS.Packet.RESPONSE:
try:
packed_response = self.decrypt(packet.data)
unpacked_response = umsgpack.unpackb(packed_response)
request_id = unpacked_response[0]
response_data = unpacked_response[1]
self.handle_response(request_id, response_data)
except Exception as e:
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
elif packet.context == RNS.Packet.LRRTT: elif packet.context == RNS.Packet.LRRTT:
if not self.initiator: if not self.initiator:
self.rtt_packet(packet) self.rtt_packet(packet)
@@ -440,7 +602,12 @@ class Link:
elif packet.context == RNS.Packet.RESOURCE_ADV: elif packet.context == RNS.Packet.RESOURCE_ADV:
packet.plaintext = self.decrypt(packet.data) packet.plaintext = self.decrypt(packet.data)
if self.resource_strategy == Link.ACCEPT_NONE:
if RNS.ResourceAdvertisement.is_request(packet):
RNS.Resource.accept(packet, callback=self.request_resource_concluded)
elif RNS.ResourceAdvertisement.is_response(packet):
RNS.Resource.accept(packet, callback=self.response_resource_concluded)
elif self.resource_strategy == Link.ACCEPT_NONE:
pass pass
elif self.resource_strategy == Link.ACCEPT_APP: elif self.resource_strategy == Link.ACCEPT_APP:
if self.callbacks.resource != None: if self.callbacks.resource != None:
@@ -537,13 +704,13 @@ class Link:
except Exception as e: except Exception as e:
return False return False
def link_established_callback(self, callback): def set_link_established_callback(self, callback):
self.callbacks.link_established = callback self.callbacks.link_established = callback
def link_closed_callback(self, callback): def set_link_closed_callback(self, callback):
self.callbacks.link_closed = callback self.callbacks.link_closed = callback
def packet_callback(self, callback): def set_packet_callback(self, callback):
""" """
Registers a function to be called when a packet has been Registers a function to be called when a packet has been
received over this link. received over this link.
@@ -552,7 +719,7 @@ class Link:
""" """
self.callbacks.packet = callback self.callbacks.packet = callback
def resource_callback(self, callback): def set_resource_callback(self, callback):
""" """
Registers a function to be called when a resource has been Registers a function to be called when a resource has been
advertised over this link. If the function returns *True* advertised over this link. If the function returns *True*
@@ -563,7 +730,7 @@ class Link:
""" """
self.callbacks.resource = callback self.callbacks.resource = callback
def resource_started_callback(self, callback): def set_resource_started_callback(self, callback):
""" """
Registers a function to be called when a resource has begun Registers a function to be called when a resource has begun
transferring over this link. transferring over this link.
@@ -572,7 +739,7 @@ class Link:
""" """
self.callbacks.resource_started = callback self.callbacks.resource_started = callback
def resource_concluded_callback(self, callback): def set_resource_concluded_callback(self, callback):
""" """
Registers a function to be called when a resource has concluded Registers a function to be called when a resource has concluded
transferring over this link. transferring over this link.
@@ -581,6 +748,15 @@ class Link:
""" """
self.callbacks.resource_concluded = callback self.callbacks.resource_concluded = callback
def set_remote_identified_callback(self, callback):
"""
Registers a function to be called when an initiating peer has
identified over this link.
:param callback: A function or method with the signature *callback(identity)* to be called.
"""
self.callbacks.remote_identified = callback
def resource_concluded(self, resource): def resource_concluded(self, resource):
if resource in self.incoming_resources: if resource in self.incoming_resources:
self.incoming_resources.remove(resource) self.incoming_resources.remove(resource)
@@ -647,3 +823,76 @@ class Link:
def __str__(self): def __str__(self):
return RNS.prettyhexrep(self.link_id) return RNS.prettyhexrep(self.link_id)
class RequestReceipt():
FAILED = 0x00
SENT = 0x01
DELIVERED = 0x02
READY = 0x03
def __init__(self, link, packet_receipt = None, resource = None, response_callback = None, failed_callback = None):
self.packet_receipt = packet_receipt
self.resource = resource
if self.packet_receipt != None:
self.hash = packet_receipt.truncated_hash
self.packet_receipt.set_timeout_callback(self.request_timed_out)
elif self.resource != None:
self.hash = resource.request_id
resource.set_callback(self.request_resource_concluded)
self.link = link
self.request_id = self.hash
self.response = None
self.status = RequestReceipt.SENT
self.sent_at = time.time()
self.timeout = RNS.Packet.TIMEOUT
self.concluded_at = None
self.callbacks = RequestReceiptCallbacks()
self.callbacks.response = response_callback
self.callbacks.failed = failed_callback
self.link.pending_requests.append(self)
def request_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE:
RNS.log("Request "+RNS.prettyhexrep(self.request_id)+" successfully sent as resource.", RNS.LOG_DEBUG)
else:
RNS.log("Sending request "+RNS.prettyhexrep(self.request_id)+" as resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
self.status = RequestReceipt.FAILED
self.concluded_at = time.time()
self.link.pending_requests.remove(self)
if self.callbacks.failed != None:
self.callbacks.failed(self)
def request_timed_out(self, packet_receipt):
self.status = RequestReceipt.FAILED
self.concluded_at = time.time()
self.link.pending_requests.remove(self)
if self.callbacks.failed != None:
self.callbacks.failed(self)
def response_received(self, response):
self.response = response
if self.packet_receipt != None:
self.packet_receipt.status = RNS.PacketReceipt.DELIVERED
self.packet_receipt.proved = True
self.packet_receipt.concluded_at = time.time()
if self.packet_receipt.callbacks.delivery != None:
self.packet_receipt.callbacks.delivery(self)
if self.callbacks.response != None:
self.callbacks.response(self)
class RequestReceiptCallbacks:
def __init__(self):
self.response = None
self.failed = None
+32 -12
View File
@@ -6,8 +6,16 @@ import RNS
class Packet: class Packet:
""" """
The Packet class is used to create packet instances that can be The Packet class is used to create packet instances that can be sent
sent over a Reticulum network. over a Reticulum network. Packets to will automatically be encrypted if
they are adressed to a ``RNS.Destination.SINGLE`` destination,
``RNS.Destination.GROUP`` destination or a :ref:`RNS.Link<api-link>`.
For ``RNS.Destination.GROUP`` destinations, Reticulum will use the
pre-shared key configured for the destination.
For ``RNS.Destination.SINGLE`` destinations and :ref:`RNS.Link<api-link>`
destinations, reticulum will use ephemeral keys, and offers **Forward Secrecy**.
:param destination: A :ref:`RNS.Destination<api-destination>` instance to which the packet will be sent. :param destination: A :ref:`RNS.Destination<api-destination>` instance to which the packet will be sent.
:param data: The data payload to be included in the packet as *bytes*. :param data: The data payload to be included in the packet as *bytes*.
@@ -33,7 +41,7 @@ class Packet:
HEADER_4 = 0x03 # Reserved HEADER_4 = 0x03 # Reserved
header_types = [HEADER_1, HEADER_2, HEADER_3, HEADER_4] header_types = [HEADER_1, HEADER_2, HEADER_3, HEADER_4]
# Data packet context types # Packet context types
NONE = 0x00 # Generic data packet NONE = 0x00 # Generic data packet
RESOURCE = 0x01 # Packet is part of a resource RESOURCE = 0x01 # Packet is part of a resource
RESOURCE_ADV = 0x02 # Packet is a resource advertisement RESOURCE_ADV = 0x02 # Packet is a resource advertisement
@@ -48,7 +56,8 @@ class Packet:
PATH_RESPONSE = 0x0B # Packet is a response to a path request PATH_RESPONSE = 0x0B # Packet is a response to a path request
COMMAND = 0x0C # Packet is a command COMMAND = 0x0C # Packet is a command
COMMAND_STATUS = 0x0D # Packet is a status of an executed command COMMAND_STATUS = 0x0D # Packet is a status of an executed command
KEEPALIVE = 0xFB # Packet is a keepalive packet KEEPALIVE = 0xFA # Packet is a keepalive packet
LINKIDENTIFY = 0xFB # Packet is a link peer identification proof
LINKCLOSE = 0xFC # Packet is a link close message LINKCLOSE = 0xFC # Packet is a link close message
LINKPROOF = 0xFD # Packet is a link packet proof LINKPROOF = 0xFD # Packet is a link packet proof
LRRTT = 0xFE # Packet is a link request round-trip time measurement LRRTT = 0xFE # Packet is a link request round-trip time measurement
@@ -56,14 +65,20 @@ class Packet:
# This is used to calculate allowable # This is used to calculate allowable
# payload sizes # payload sizes
HEADER_MAXSIZE = 23 HEADER_MAXSIZE = RNS.Reticulum.HEADER_MAXSIZE
MDU = RNS.Reticulum.MDU MDU = RNS.Reticulum.MDU
# With an MTU of 500, the maximum RSA-encrypted # With an MTU of 500, the maximum of data we can
# amount of data we can send in a single packet # send in a single encrypted packet is given by
# is given by the below calculation; 258 bytes. # the below calculation; 383 bytes.
RSA_MDU = math.floor(MDU/RNS.Identity.DECRYPT_CHUNKSIZE)*RNS.Identity.ENCRYPT_CHUNKSIZE ENCRYPTED_MDU = math.floor((RNS.Reticulum.MDU-RNS.Identity.AES_HMAC_OVERHEAD-RNS.Identity.KEYSIZE//16)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1
"""
The maximum size of the payload data in a single encrypted packet
"""
PLAIN_MDU = MDU PLAIN_MDU = MDU
"""
The maximum size of the payload data in a single unencrypted packet
"""
# TODO: This should be calculated # TODO: This should be calculated
# more intelligently # more intelligently
@@ -128,6 +143,9 @@ class Packet:
if self.packet_type == Packet.ANNOUNCE: if self.packet_type == Packet.ANNOUNCE:
# Announce packets are not encrypted # Announce packets are not encrypted
self.ciphertext = self.data self.ciphertext = self.data
elif self.packet_type == Packet.LINKREQUEST:
# Link request packets are not encrypted
self.ciphertext = self.data
elif self.packet_type == Packet.PROOF and self.context == Packet.RESOURCE_PRF: elif self.packet_type == Packet.PROOF and self.context == Packet.RESOURCE_PRF:
# Resource proofs are not encrypted # Resource proofs are not encrypted
self.ciphertext = self.data self.ciphertext = self.data
@@ -171,6 +189,7 @@ class Packet:
self.packed = True self.packed = True
self.update_hash() self.update_hash()
def unpack(self): def unpack(self):
self.flags = self.raw[0] self.flags = self.raw[0]
self.hops = self.raw[1] self.hops = self.raw[1]
@@ -307,6 +326,7 @@ class PacketReceipt:
# Creates a new packet receipt from a sent packet # Creates a new packet receipt from a sent packet
def __init__(self, packet): def __init__(self, packet):
self.hash = packet.get_hash() self.hash = packet.get_hash()
self.truncated_hash = packet.getTruncatedHash()
self.sent = True self.sent = True
self.sent_at = time.time() self.sent_at = time.time()
self.timeout = Packet.TIMEOUT self.timeout = Packet.TIMEOUT
@@ -406,7 +426,7 @@ class PacketReceipt:
else: else:
return False return False
def rtt(self): def get_rtt(self):
""" """
:returns: The round-trip-time in seconds :returns: The round-trip-time in seconds
""" """
@@ -439,7 +459,7 @@ class PacketReceipt:
""" """
self.timeout = float(timeout) self.timeout = float(timeout)
def delivery_callback(self, callback): def set_delivery_callback(self, callback):
""" """
Sets a function that gets called if a successfull delivery has been proven. Sets a function that gets called if a successfull delivery has been proven.
@@ -449,7 +469,7 @@ class PacketReceipt:
# Set a function that gets called if the # Set a function that gets called if the
# delivery times out # delivery times out
def timeout_callback(self, callback): def set_timeout_callback(self, callback):
""" """
Sets a function that gets called if the delivery times out. Sets a function that gets called if the delivery times out.
+58 -8
View File
@@ -17,11 +17,12 @@ class Resource:
:param link: The :ref:`RNS.Link<api-link>` instance on which to transfer the data. :param link: The :ref:`RNS.Link<api-link>` instance on which to transfer the data.
:param advertise: Whether to automatically advertise the resource. Can be *True* or *False*. :param advertise: Whether to automatically advertise the resource. Can be *True* or *False*.
:param auto_compress: Whether to auto-compress the resource. Can be *True* or *False*. :param auto_compress: Whether to auto-compress the resource. Can be *True* or *False*.
:param auto_compress: Whether the resource must be compressed. Can be *True* or *False*. Used for debugging, will disappear in the future.
:param callback: A *callable* with the signature *callback(resource)*. Will be called when the resource transfer concludes. :param callback: A *callable* with the signature *callback(resource)*. Will be called when the resource transfer concludes.
:param progress_callback: A *callable* with the signature *callback(resource)*. Will be called whenever the resource transfer progress is updated. :param progress_callback: A *callable* with the signature *callback(resource)*. Will be called whenever the resource transfer progress is updated.
:param segment_index: Internal use, ignore. :param segment_index: Internal use, ignore.
:param original_hash: Internal use, ignore. :param original_hash: Internal use, ignore.
:param is_request: Internal use, ignore.
:param is_response: Internal use, ignore.
""" """
WINDOW_FLEXIBILITY = 4 WINDOW_FLEXIBILITY = 4
WINDOW_MIN = 1 WINDOW_MIN = 1
@@ -120,6 +121,7 @@ class Resource:
resource.link.register_incoming_resource(resource) resource.link.register_incoming_resource(resource)
RNS.log("Accepting resource advertisement for "+RNS.prettyhexrep(resource.hash), RNS.LOG_DEBUG) RNS.log("Accepting resource advertisement for "+RNS.prettyhexrep(resource.hash), RNS.LOG_DEBUG)
if resource.link.callbacks.resource_started != None:
resource.link.callbacks.resource_started(resource) resource.link.callbacks.resource_started(resource)
resource.hashmap_update(0, resource.hashmap_raw) resource.hashmap_update(0, resource.hashmap_raw)
@@ -134,7 +136,7 @@ class Resource:
# Create a resource for transmission to a remote destination # Create a resource for transmission to a remote destination
# The data passed can be either a bytes-array or a file opened # The data passed can be either a bytes-array or a file opened
# in binary read mode. # in binary read mode.
def __init__(self, data, link, advertise=True, auto_compress=True, must_compress=False, callback=None, progress_callback=None, segment_index = 1, original_hash = None): def __init__(self, data, link, advertise=True, auto_compress=True, callback=None, progress_callback=None, segment_index = 1, original_hash = None, request_id = None, is_response = False):
data_size = None data_size = None
resource_data = None resource_data = None
if hasattr(data, "read"): if hasattr(data, "read"):
@@ -189,6 +191,8 @@ class Resource:
self.__watchdog_job_id = 0 self.__watchdog_job_id = 0
self.__progress_callback = progress_callback self.__progress_callback = progress_callback
self.rtt = None self.rtt = None
self.request_id = request_id
self.is_response = is_response
self.receiver_min_consecutive_height = 0 self.receiver_min_consecutive_height = 0
@@ -198,7 +202,7 @@ class Resource:
self.uncompressed_data = data self.uncompressed_data = data
compression_began = time.time() compression_began = time.time()
if must_compress or (auto_compress and len(self.uncompressed_data) < Resource.AUTO_COMPRESS_MAX_SIZE): if (auto_compress and len(self.uncompressed_data) < Resource.AUTO_COMPRESS_MAX_SIZE):
RNS.log("Compressing resource data...", RNS.LOG_DEBUG) RNS.log("Compressing resource data...", RNS.LOG_DEBUG)
self.compressed_data = bz2.compress(self.uncompressed_data) self.compressed_data = bz2.compress(self.uncompressed_data)
RNS.log("Compression completed in "+str(round(time.time()-compression_began, 3))+" seconds", RNS.LOG_DEBUG) RNS.log("Compression completed in "+str(round(time.time()-compression_began, 3))+" seconds", RNS.LOG_DEBUG)
@@ -251,6 +255,7 @@ class Resource:
self.random_hash = RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE] self.random_hash = RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE]
self.hash = RNS.Identity.full_hash(data+self.random_hash) self.hash = RNS.Identity.full_hash(data+self.random_hash)
self.truncated_hash = RNS.Identity.truncated_hash(data+self.random_hash)
self.expected_proof = RNS.Identity.full_hash(data+self.hash) self.expected_proof = RNS.Identity.full_hash(data+self.hash)
if original_hash == None: if original_hash == None:
@@ -740,6 +745,9 @@ class Resource:
self.link.resource_concluded(self) self.link.resource_concluded(self)
self.callback(self) self.callback(self)
def set_callback(self, callback):
self.callback = callback
def progress_callback(self, callback): def progress_callback(self, callback):
self.__progress_callback = callback self.__progress_callback = callback
@@ -748,8 +756,6 @@ class Resource:
:returns: The current progress of the resource transfer as a *float* between 0.0 and 1.0. :returns: The current progress of the resource transfer as a *float* between 0.0 and 1.0.
""" """
if self.initiator: if self.initiator:
# TODO: Remove
# progress = self.sent_parts / len(self.parts)
self.processed_parts = (self.segment_index-1)*math.ceil(Resource.MAX_EFFICIENT_SIZE/Resource.SDU) self.processed_parts = (self.segment_index-1)*math.ceil(Resource.MAX_EFFICIENT_SIZE/Resource.SDU)
self.processed_parts += self.sent_parts self.processed_parts += self.sent_parts
self.progress_total_parts = float(self.grand_total_parts) self.progress_total_parts = float(self.grand_total_parts)
@@ -770,10 +776,35 @@ class Resource:
class ResourceAdvertisement: class ResourceAdvertisement:
HASHMAP_MAX_LEN = 73 HASHMAP_MAX_LEN = 70
COLLISION_GUARD_SIZE = 2*Resource.WINDOW_MAX+HASHMAP_MAX_LEN COLLISION_GUARD_SIZE = 2*Resource.WINDOW_MAX+HASHMAP_MAX_LEN
def __init__(self, resource=None): @staticmethod
def is_request(advertisement_packet):
adv = ResourceAdvertisement.unpack(advertisement_packet.plaintext)
if adv.q != None and adv.u:
return True
else:
return False
@staticmethod
def is_response(advertisement_packet):
adv = ResourceAdvertisement.unpack(advertisement_packet.plaintext)
if adv.q != None and adv.p:
return True
else:
return False
@staticmethod
def get_request_id(advertisement_packet):
adv = ResourceAdvertisement.unpack(advertisement_packet.plaintext)
return adv.q
def __init__(self, resource=None, request_id=None, is_response=False):
if resource != None: if resource != None:
self.t = resource.size # Transfer size self.t = resource.size # Transfer size
self.d = resource.total_size # Total uncompressed data size self.d = resource.total_size # Total uncompressed data size
@@ -787,7 +818,21 @@ class ResourceAdvertisement:
self.s = resource.split # Split flag self.s = resource.split # Split flag
self.i = resource.segment_index # Segment index self.i = resource.segment_index # Segment index
self.l = resource.total_segments # Total segments self.l = resource.total_segments # Total segments
self.f = 0x00 | self.s << 2 | self.c << 1 | self.e # Flags self.q = resource.request_id # ID of associated request
self.u = False # Is request flag
self.p = False # Is response flag
if self.q != None:
if not resource.is_response:
self.u = True
self.p = False
else:
self.u = False
self.p = True
# Flags
self.f = 0x00 | self.p << 4 | self.u << 3 | self.s << 2 | self.c << 1 | self.e
def pack(self, segment=0): def pack(self, segment=0):
hashmap_start = segment*ResourceAdvertisement.HASHMAP_MAX_LEN hashmap_start = segment*ResourceAdvertisement.HASHMAP_MAX_LEN
@@ -806,12 +851,14 @@ class ResourceAdvertisement:
"o": self.o, # Original hash "o": self.o, # Original hash
"i": self.i, # Segment index "i": self.i, # Segment index
"l": self.l, # Total segments "l": self.l, # Total segments
"q": self.q, # Request ID
"f": self.f, # Resource flags "f": self.f, # Resource flags
"m": hashmap "m": hashmap
} }
return umsgpack.packb(dictionary) return umsgpack.packb(dictionary)
@staticmethod @staticmethod
def unpack(data): def unpack(data):
dictionary = umsgpack.unpackb(data) dictionary = umsgpack.unpackb(data)
@@ -827,8 +874,11 @@ class ResourceAdvertisement:
adv.f = dictionary["f"] adv.f = dictionary["f"]
adv.i = dictionary["i"] adv.i = dictionary["i"]
adv.l = dictionary["l"] adv.l = dictionary["l"]
adv.q = dictionary["q"]
adv.e = True if (adv.f & 0x01) == 0x01 else False adv.e = True if (adv.f & 0x01) == 0x01 else False
adv.c = True if ((adv.f >> 1) & 0x01) == 0x01 else False adv.c = True if ((adv.f >> 1) & 0x01) == 0x01 else False
adv.s = True if ((adv.f >> 2) & 0x01) == 0x01 else False adv.s = True if ((adv.f >> 2) & 0x01) == 0x01 else False
adv.u = True if ((adv.f >> 3) & 0x01) == 0x01 else False
adv.p = True if ((adv.f >> 4) & 0x01) == 0x01 else False
return adv return adv
+73 -19
View File
@@ -217,13 +217,27 @@ class Reticulum:
try: try:
if ("interface_enabled" in c) and c.as_bool("interface_enabled") == True: if ("interface_enabled" in c) and c.as_bool("interface_enabled") == True:
if c["type"] == "UDPInterface": if c["type"] == "UDPInterface":
device = c["device"] if "device" in c else None
port = int(c["port"]) if "port" in c else None
listen_ip = c["listen_ip"] if "listen_ip" in c else None
listen_port = int(c["listen_port"]) if "listen_port" in c else None
forward_ip = c["forward_ip"] if "forward_ip" in c else None
forward_port = int(c["forward_port"]) if "forward_port" in c else None
if port != None:
if listen_port == None:
listen_port = port
if forward_port == None:
forward_port = port
interface = UDPInterface.UDPInterface( interface = UDPInterface.UDPInterface(
RNS.Transport, RNS.Transport,
name, name,
c["listen_ip"], device,
int(c["listen_port"]), listen_ip,
c["forward_ip"], listen_port,
int(c["forward_port"]) forward_ip,
forward_port
) )
if "outgoing" in c and c.as_bool("outgoing") == True: if "outgoing" in c and c.as_bool("outgoing") == True:
@@ -235,11 +249,20 @@ class Reticulum:
if c["type"] == "TCPServerInterface": if c["type"] == "TCPServerInterface":
device = c["device"] if "device" in c else None
port = int(c["port"]) if "port" in c else None
listen_ip = c["listen_ip"] if "listen_ip" in c else None
listen_port = int(c["listen_port"]) if "listen_port" in c else None
if port != None:
listen_port = port
interface = TCPInterface.TCPServerInterface( interface = TCPInterface.TCPServerInterface(
RNS.Transport, RNS.Transport,
name, name,
c["listen_ip"], device,
int(c["listen_port"]) listen_ip,
listen_port
) )
if "outgoing" in c and c.as_bool("outgoing") == True: if "outgoing" in c and c.as_bool("outgoing") == True:
@@ -482,10 +505,10 @@ allow_unencrypted = False
# If you enable Transport, your system will route traffic # If you enable Transport, your system will route traffic
# for other peers, pass announces and serve path requests. # for other peers, pass announces and serve path requests.
# Unless you really know what you're doing, this should be # This should be done for systems that are suited to act
# done only for systems that are suited to act as transport # as transport nodes, ie. if they are stationary and
# nodes, ie. if they are stationary and always-on. This # always-on. This directive is optional and can be removed
# directive is optional and can be removed for brevity. # for brevity.
enable_transport = False enable_transport = False
@@ -533,20 +556,37 @@ loglevel = 4
[interfaces] [interfaces]
# This interface enables communication with other # This interface enables communication with other
# Reticulum nodes on your local ethernet networks. # local Reticulum nodes over UDP. You can modify it
# It's enabled by default, and provides basic # to suit your needs or turn it off completely.
# connectivity to other peers in your local ethernet # As a minimum, you should probably specify the
# broadcast domain. You can modify it to suit your # network device you want to communicate on, such
# needs or turn it off completely. # as eth0 or wlan0.
[[Default UDP Interface]] [[Default UDP Interface]]
type = UDPInterface type = UDPInterface
interface_enabled = True interface_enabled = True
outgoing = True outgoing = True
listen_ip = 0.0.0.0 device = eth0
listen_port = 4242 port = 4242
forward_ip = 255.255.255.255
forward_port = 4242 # Assuming the eth0 device has the address
# 10.55.0.72/24, the above configuration would
# be equivalent to the following manual setup.
# Note that we are both listening and forwarding
# to the network segments broadcast address.
# listen_ip = 10.55.0.255
# listen_port = 4242
# forward_ip = 10.55.0.255
# forward_port = 4242
# You can of course also communicate only with
# a single IP address
# listen_ip = 10.55.0.15
# listen_port = 4242
# forward_ip = 10.55.0.16
# forward_port = 4242
# This example demonstrates a TCP server interface. # This example demonstrates a TCP server interface.
@@ -557,9 +597,23 @@ loglevel = 4
type = TCPServerInterface type = TCPServerInterface
interface_enabled = False interface_enabled = False
outgoing = True outgoing = True
# This configuration will listen on all IP
# interfaces on port 4242
listen_ip = 0.0.0.0 listen_ip = 0.0.0.0
listen_port = 4242 listen_port = 4242
# Alternatively you can bind to a specific IP
# listen_ip = 10.0.0.88
# listen_port = 4242
# Or a specific network device
# device = eth0
# port = 4242
# To connect to a TCP server interface, you would # To connect to a TCP server interface, you would
# naturally use the TCP client interface. Here's # naturally use the TCP client interface. Here's
+15 -6
View File
@@ -22,8 +22,6 @@ class Transport:
APP_NAME = "rnstransport" APP_NAME = "rnstransport"
# TODO: Document the addition of random windows
# and max local rebroadcasts.
PATHFINDER_M = 18 # Max hops PATHFINDER_M = 18 # Max hops
PATHFINDER_C = 2.0 # Decay constant PATHFINDER_C = 2.0 # Decay constant
PATHFINDER_R = 1 # Retransmit retries PATHFINDER_R = 1 # Retransmit retries
@@ -51,6 +49,8 @@ class Transport:
receipts = [] # Receipts of all outgoing packets for proof processing receipts = [] # Receipts of all outgoing packets for proof processing
# TODO: "destination_table" should really be renamed to "path_table" # TODO: "destination_table" should really be renamed to "path_table"
# Notes on memory usage: 1 megabyte of memory can store approximately
# 55.100 path table entries or approximately 22.300 link table entries.
announce_table = {} # A table for storing announces currently waiting to be retransmitted announce_table = {} # A table for storing announces currently waiting to be retransmitted
destination_table = {} # A lookup table containing the next hop to a given destination destination_table = {} # A lookup table containing the next hop to a given destination
reverse_table = {} # A lookup table for storing packet hashes used to return proofs and replies reverse_table = {} # A lookup table for storing packet hashes used to return proofs and replies
@@ -108,7 +108,7 @@ class Transport:
# Create transport-specific destinations # Create transport-specific destinations
Transport.path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request") Transport.path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
Transport.path_request_destination.packet_callback(Transport.path_request_handler) Transport.path_request_destination.set_packet_callback(Transport.path_request_handler)
Transport.control_destinations.append(Transport.path_request_destination) Transport.control_destinations.append(Transport.path_request_destination)
Transport.control_hashes.append(Transport.path_request_destination.hash) Transport.control_hashes.append(Transport.path_request_destination.hash)
@@ -377,6 +377,7 @@ class Transport:
# just the relevant interface if the packet has an attached # just the relevant interface if the packet has an attached
# interface, or belongs to a link. # interface, or belongs to a link.
else: else:
stored_hash = False
for interface in Transport.interfaces: for interface in Transport.interfaces:
if interface.OUT: if interface.OUT:
should_transmit = True should_transmit = True
@@ -391,6 +392,10 @@ class Transport:
if should_transmit: if should_transmit:
RNS.log("Transmitting "+str(len(packet.raw))+" bytes on: "+str(interface), RNS.LOG_EXTREME) RNS.log("Transmitting "+str(len(packet.raw))+" bytes on: "+str(interface), RNS.LOG_EXTREME)
RNS.log("Hash is "+RNS.prettyhexrep(packet.packet_hash), RNS.LOG_EXTREME) RNS.log("Hash is "+RNS.prettyhexrep(packet.packet_hash), RNS.LOG_EXTREME)
if not stored_hash:
Transport.packet_hashlist.append(packet.packet_hash)
interface.processOutgoing(packet.raw) interface.processOutgoing(packet.raw)
sent = True sent = True
@@ -652,7 +657,7 @@ class Transport:
# First, check that the announce is not for a destination # First, check that the announce is not for a destination
# local to this system, and that hops are less than the max # local to this system, and that hops are less than the max
if (not any(packet.destination_hash == d.hash for d in Transport.destinations) and packet.hops < Transport.PATHFINDER_M+1): if (not any(packet.destination_hash == d.hash for d in Transport.destinations) and packet.hops < Transport.PATHFINDER_M+1):
random_blob = packet.data[RNS.Identity.DERKEYSIZE//8+10:RNS.Identity.DERKEYSIZE//8+20] random_blob = packet.data[RNS.Identity.KEYSIZE//8+10:RNS.Identity.KEYSIZE//8+20]
random_blobs = [] random_blobs = []
if packet.destination_hash in Transport.destination_table: if packet.destination_hash in Transport.destination_table:
random_blobs = Transport.destination_table[packet.destination_hash][4] random_blobs = Transport.destination_table[packet.destination_hash][4]
@@ -1138,13 +1143,17 @@ class Transport:
@staticmethod @staticmethod
def exit_handler(): def exit_handler():
RNS.log("Saving packet hashlist to storage...", RNS.LOG_VERBOSE)
try: try:
if not RNS.Reticulum.transport_enabled():
Transport.packet_hashlist = []
else:
RNS.log("Saving packet hashlist to storage...", RNS.LOG_VERBOSE)
packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist" packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist"
file = open(packet_hashlist_path, "wb") file = open(packet_hashlist_path, "wb")
file.write(umsgpack.packb(Transport.packet_hashlist)) file.write(umsgpack.packb(Transport.packet_hashlist))
file.close() file.close()
RNS.log("Done packet hashlist to storage", RNS.LOG_VERBOSE)
except Exception as e: except Exception as e:
RNS.log("Could not save packet hashlist to storage, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log("Could not save packet hashlist to storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
+7 -2
View File
@@ -5,6 +5,8 @@ import time
import random import random
import threading import threading
from ._version import __version__
from .Reticulum import Reticulum from .Reticulum import Reticulum
from .Identity import Identity from .Identity import Identity
from .Link import Link from .Link import Link
@@ -12,7 +14,7 @@ from .Transport import Transport
from .Destination import Destination from .Destination import Destination
from .Packet import Packet from .Packet import Packet
from .Packet import PacketReceipt from .Packet import PacketReceipt
from .Resource import Resource from .Resource import Resource, ResourceAdvertisement
modules = glob.glob(os.path.dirname(__file__)+"/*.py") modules = glob.glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')] __all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]
@@ -60,6 +62,9 @@ def loglevelname(level):
return "Unknown" return "Unknown"
def version():
return __version__
def log(msg, level=3, _override_destination = False): def log(msg, level=3, _override_destination = False):
global _always_override_destination global _always_override_destination
@@ -68,7 +73,7 @@ def log(msg, level=3, _override_destination = False):
logstring = "["+time.strftime(logtimefmt)+"] ["+loglevelname(level)+"] "+msg logstring = "["+time.strftime(logtimefmt)+"] ["+loglevelname(level)+"] "+msg
logging_lock.acquire() logging_lock.acquire()
if (logdest == LOG_STDOUT or _always_override_destination): if (logdest == LOG_STDOUT or _always_override_destination or _override_destination):
print(logstring) print(logstring)
logging_lock.release() logging_lock.release()
+1
View File
@@ -0,0 +1 @@
__version__ = "0.2.2"
Binary file not shown.
+1 -1
View File
@@ -1,4 +1,4 @@
# Sphinx build info version 1 # Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 010a10c5bc670583cef4151858e38839 config: 966ae7177c1d48c9ee15971994c623b5
tags: 645f666f9bcd5a90fca523b33c5a78b7 tags: 645f666f9bcd5a90fca523b33c5a78b7
Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

+23
View File
@@ -68,6 +68,29 @@ destination, and passing traffic back and forth over the link.
This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py>`_. This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py>`_.
.. _example-identify:
Identification
==============
The *Identify* example explores identifying an intiator of a link, once
the link has been established.
.. literalinclude:: ../../Examples/Identify.py
This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Identify.py>`_.
.. _example-request:
Requests & Responses
====================
The *Request* example explores sendig requests and receiving responses.
.. literalinclude:: ../../Examples/Request.py
This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Request.py>`_.
.. _example-filetransfer: .. _example-filetransfer:
Filetransfer Filetransfer
@@ -1,7 +1,6 @@
******************** ********************
Getting Started Fast Getting Started Fast
******************** ********************
What do we want to do? Something! When do we want to do it? Right now! Let's go.
The best way to get started with the Reticulum Network Stack depends on what The best way to get started with the Reticulum Network Stack depends on what
you want to do. This guide will outline sensible starting paths for different you want to do. This guide will outline sensible starting paths for different
@@ -23,7 +22,7 @@ in the development for the messaging and information-sharing protocol
Develop a Program with Reticulum Develop a Program with Reticulum
=========================================== ===========================================
If you want to develop programs that use Reticulum, the easiest way to get If you want to develop programs that use Reticulum, the easiest way to get
started is to install Reticulum via pip: started is to install the latest release of Reticulum via pip:
.. code:: .. code::
+2 -2
View File
@@ -10,9 +10,9 @@ the development of Reticulum itself.
whatis whatis
gettingstartedfast gettingstartedfast
examples
reference
understanding understanding
reference
examples
Indices and Tables Indices and Tables
+139 -92
View File
@@ -160,9 +160,13 @@ Destination Naming
Destinations are created and named in an easy to understand dotted notation of *aspects*, and Destinations are created and named in an easy to understand dotted notation of *aspects*, and
represented on the network as a hash of this value. The hash is a SHA-256 truncated to 80 bits. The represented on the network as a hash of this value. The hash is a SHA-256 truncated to 80 bits. The
top level aspect should always be a unique identifier for the application using the destination. top level aspect should always be a unique identifier for the application using the destination.
The next levels of aspects can be defined in any way by the creator of the application. For example, The next levels of aspects can be defined in any way by the creator of the application.
a destination for a environmental monitoring application could be made up of the application name, a
device type and measurement type, like this: Aspects can be as long and as plentiful as required, and a resulting long destination name will not
impact efficiency, as names are always represented as truncated SHA-256 hashes on the network.
As an example, a destination for a environmental monitoring application could be made up of the
application name, a device type and measurement type, like this:
.. code-block:: text .. code-block:: text
@@ -201,9 +205,8 @@ To recap, the different destination types should be used in the following situat
* **Single** * **Single**
When private communication between two endpoints is needed. Supports multiple hops. When private communication between two endpoints is needed. Supports multiple hops.
* **Group** * **Group**
When private communication between two or more endpoints is needed. More efficient in When private communication between two or more endpoints is needed. Supports multiple hops
data usage than *single* destinations. Supports multiple hops indirectly, but must first be indirectly, but must first be established through a *single* destination.
established through a *single* destination.
* **Plain** * **Plain**
When plain-text communication is desirable, for example when broadcasting information. When plain-text communication is desirable, for example when broadcasting information.
@@ -214,9 +217,9 @@ an unknown public key from the network, as all participating nodes serve as a di
of public keys. of public keys.
Note that public key information can be shared and verified in many other ways than using the Note that public key information can be shared and verified in many other ways than using the
built-in methodology, and that it is therefore not required to use the announce/request functionality. built-in *announce* functionality, and that it is therefore not required to use the announce/request
It is by far the easiest though, and should definitely be used if there is not a good reason for functionality to obtain public keys. It is by far the easiest though, and should definitely be used
doing it differently. if there is not a good reason for doing it differently.
.. _understanding-keyannouncements: .. _understanding-keyannouncements:
@@ -235,7 +238,7 @@ contain the following information:
* The announcers public key * The announcers public key
* Application specific data, in this case the users nickname and availability status * Application specific data, in this case the users nickname and availability status
* A random blob, making each new announce unique * A random blob, making each new announce unique
* A signature of the above information, verifying authenticity * An Ed25519 signature of the above information, verifying authenticity
With this information, any Reticulum node that receives it will be able to reconstruct an outgoing With this information, any Reticulum node that receives it will be able to reconstruct an outgoing
destination to securely communicate with that destination. You might have noticed that there is one destination to securely communicate with that destination. You might have noticed that there is one
@@ -244,8 +247,9 @@ the aspect names of the destination. These are intentionally left out to save ba
will be implicit in almost all cases. If a destination name is not entirely implicit, information can be will be implicit in almost all cases. If a destination name is not entirely implicit, information can be
included in the application specific data part that will allow the receiver to infer the naming. included in the application specific data part that will allow the receiver to infer the naming.
It is important to note that announcements will be forwarded throughout the network according to a It is important to note that announces will be forwarded throughout the network according to a
certain pattern. This will be detailed later. certain pattern. This will be detailed in the section
:ref:`The Announce Mechanism in Detail<understanding-announce>`.
Seeing how *single* destinations are always tied to a private/public key pair leads us to the next topic. Seeing how *single* destinations are always tied to a private/public key pair leads us to the next topic.
@@ -268,8 +272,8 @@ the identity first, and then link it to created destinations.
Building upon the simple messenger example, we could use an identity to represent the user of the Building upon the simple messenger example, we could use an identity to represent the user of the
application. Destinations created will then be linked to this identity to allow communication to application. Destinations created will then be linked to this identity to allow communication to
reach the user. In such a case it is of great importance to store the users identity securely and reach the user. In all cases it is of great importance to store the private keys associated with any
privately. Reticulum Identity securely and privately.
.. _understanding-gettingfurther: .. _understanding-gettingfurther:
@@ -279,8 +283,9 @@ Getting Further
The above functions and principles form the core of Reticulum, and would suffice to create The above functions and principles form the core of Reticulum, and would suffice to create
functional networked applications in local clusters, for example over radio links where all interested functional networked applications in local clusters, for example over radio links where all interested
nodes can directly hear each other. But to be truly useful, we need a way to direct traffic over multiple nodes can directly hear each other. But to be truly useful, we need a way to direct traffic over multiple
hops in the network. In the next sections, two concepts that allow this will be introduced, *paths* and hops in the network.
*links*.
In the following sections, two concepts that allow this will be introduced, *paths* and *links*.
.. _understanding-transport: .. _understanding-transport:
@@ -298,70 +303,28 @@ useable over bandwidth-limited, high-latency links.
To overcome such challenges, Reticulums *Transport* system uses public-key cryptography to To overcome such challenges, Reticulums *Transport* system uses public-key cryptography to
implement the concept of *paths* that allow discovery of how to get information to a certain implement the concept of *paths* that allow discovery of how to get information to a certain
destination, and *resources* that help make reliable data transfer more efficient. destination. It is important to note that no single node in a Reticulum network knows the complete
path to a destination. Every Transport node participating in a Reticulum network will only
know what the most direct way to get a packet one hop closer to it's destination is.
.. _understanding-paths: .. _understanding-announce:
Reaching the Destination The Announce Mechanism in Detail
------------------------ --------------------------------
In networks with changing topology and trustless connectivity, nodes need a way to establish When an *announce* is transmitted by a node, it will be forwarded by any node receiving it, but
*verified connectivity* with each other. Since the network is assumed to be trustless, Reticulum according to some specific rules:
must provide a way to guarantee that the peer you are communicating with is actually who you
expect. To do this, the following process is employed:
* | First, the node that wishes to establish connectivity will send out a special packet, that * | If this exact announce has already been received before, ignore it.
traverses the network and locates the desired destination. Along the way, the nodes that
forward the packet will take note of this *link request*.
* | Second, if the destination accepts the *link request* , it will send back a packet that proves the * | If not, record into a table which node the announce was received from, and how many times in
authenticity of its identity (and the receipt of the link request) to the initiating node. All
nodes that initially forwarded the packet will also be able to verify this proof, and thus
accept the validity of the *link* throughout the network.
* | When the validity of the *link* has been accepted by forwarding nodes, these nodes will
remember the *link* , and it can subsequently be used by referring to a hash representing it.
* | As a part of the *link request* , a Diffie-Hellman key exchange takes place, that sets up an
efficient symmetrically encrypted tunnel between the two nodes, using elliptic curve
cryptography. As such, this mode of communication is preferred, even for situations when
nodes can directly communicate, when the amount of data to be exchanged numbers in the
tens of packets.
* | When a *link* has been set up, it automatically provides message receipt functionality, so the
sending node can obtain verified confirmation that the information reached the intended
recipient.
In a moment, we will discuss the specifics of how this methodology is implemented, but lets first
recap what purposes this serves. We first ensure that the node answering our request is actually the
one we want to communicate with, and not a malicious actor pretending to be so. At the same time
we establish an efficient encrypted channel. The setup of this is relatively cheap in terms of
bandwidth, so it can be used just for a short exchange, and then recreated as needed, which will also
rotate encryption keys, but the link can also be kept alive for longer periods of time, if this is
more suitable to the application. The amount of bandwidth used on keeping a link open is practically
negligible. The procedure also inserts the *link id* , a hash calculated from the link request packet,
into the memory of forwarding nodes, which means that the communicating nodes can thereafter reach each
other simply by referring to this *link id*.
Step 1: Pathfinding
^^^^^^^^^^^^^^^^^^^
The pathfinding method builds on the *announce* functionality discussed earlier. When an announce
is sent out by a node, it will be forwarded by any node receiving it, but according to some specific
rules:
* | If this announce has already been received before, ignore it.
* | Record into a table which node the announce was received from, and how many times in
total it has been retransmitted to get here. total it has been retransmitted to get here.
* | If the announce has been retransmitted *m+1* times, it will not be forwarded. By default, *m* is * | If the announce has been retransmitted *m+1* times, it will not be forwarded. By default, *m* is
set to 18. set to 18.
* | The announce will be assigned a delay *d* = c\ :sup:`h` seconds, where *c* is a decay constant, by * | The announce will be assigned a delay *d* = c\ :sup:`h` seconds, where *c* is a decay constant, and *h* is the amount of times this packet has already been forwarded.
default 2, and *h* is the amount of times this packet has already been forwarded.
* | The packet will be given a priority *p = 1/d*. * | The packet will be given a priority *p = 1/d*.
@@ -370,10 +333,11 @@ rules:
not utilized by other traffic, the announce will be forwarded. not utilized by other traffic, the announce will be forwarded.
* | If no other nodes are heard retransmitting the announce with a greater hop count than when * | If no other nodes are heard retransmitting the announce with a greater hop count than when
it left this node, transmitting it will be retried *r* times. By default, *r* is set to 2. Retries follow it left this node, transmitting it will be retried *r* times. By default, *r* is set to 1. Retries
same rules as above, with the exception that it must wait for at least *d* = c\ :sup:`h+1` + t seconds, ie., follow same rules as above, with the exception that it must wait for at least *d* = c\ :sup:`h+1` +
the amount of time it would take the next node to retransmit the packet. By default, *t* is set to t + rand(0, rw) seconds. This amount of time is equal to the amount of time it would take the next
10. node to retransmit the packet, plus a random window. By default, *t* is set to 10 seconds, and the
random window *rw* is set to 10 seconds.
* | If a newer announce from the same destination arrives, while an identical one is already in * | If a newer announce from the same destination arrives, while an identical one is already in
the queue, the newest announce is discarded. If the newest announce contains different the queue, the newest announce is discarded. If the newest announce contains different
@@ -392,14 +356,95 @@ distance of *Lavg =* 15 kilometers, an announce will be able to propagate outwar
kilometers in 34 minutes, and a *maximum announce radius* of 270 kilometers in approximately 3 kilometers in 34 minutes, and a *maximum announce radius* of 270 kilometers in approximately 3
days. days.
Step 2: Link Establishment .. _understanding-paths:
^^^^^^^^^^^^^^^^^^^^^^^^^^
After seeing how the conditions for finding a path through the network are created, we will now Reaching the Destination
explore how two nodes can establish reliable communications over multiple hops. The *link* in ------------------------
Reticulum terminology should not be viewed as a direct node-to-node link on the physical layer, but
as an abstract channel, that can be open for any amount of time, and can span an arbitrary number In networks with changing topology and trustless connectivity, nodes need a way to establish
of hops, where information will be exchanged between two nodes. *verified connectivity* with each other. Since the network is assumed to be trustless, Reticulum
must provide a way to guarantee that the peer you are communicating with is actually who you
expect. Reticulum offers two ways to do this.
For exchanges of small amounts of information, Reticulum offers the *Packet* API, which works exactly like you would expect - on a per packet level. The following process is employed when sending a packet:
* | A packet is always created with an associated destination and some payload data. When the packet is sent
to a *single* destination type, Reticulum will automatically create an ephemeral encryption key, perform
an ECDH key exchange with the destinations public key, and encrypt the information.
* | It is important to note that this key exchange does not require any network traffic. The sender already
knows the public key of the destination from an earlier received *announce*, and can thus perform the ECDH
key exchange locally, before sending the packet.
* | The public part of the newly generated ephemeral key-pair is included with the encrypted token, and sent
along with the encrypted payload data in the packet.
* | When the destination receives the packet, it can itself perform an ECDH key exchange and decrypt the
packet.
* | A new ephemeral key is used for every packet sent in this way, and forward secrecy is guaranteed on a
per packet level.
* | Once the packet has been received and decrypted by the addressed destination, that destination can opt
to *prove* its receipt of the packet. It does this by calculating the SHA-256 hash of the received packet,
and signing this hash with it's Ed25519 signing key. Transport nodes in the network can then direct this
*proof* back to the packets origin, where the signature can be verified against the destinations known
public signing key.
* | In case the packet is addressed to a *group* destination type, the packet will be encrypted with the
pre-shared AES-128 key associated with the destination. In case the packet is addressed to a *plain*
destination type, the payload data will not be encrypted. Neither of these two destination types offer
forward secrecy. In general, it is recommended to always use the *single* destination type, unless it is
strictly necessary to use one of the others.
For exchanges of larger amounts of data, or when longer sessions of bidirectional communication is desired, Reticulum offers the *Link* API. To establish a *link*, the following process is employed:
* | First, the node that wishes to establish a link will send out a special packet, that
traverses the network and locates the desired destination. Along the way, the nodes that
forward the packet will take note of this *link request*.
* | Second, if the destination accepts the *link request* , it will send back a packet that proves the
authenticity of its identity (and the receipt of the link request) to the initiating node. All
nodes that initially forwarded the packet will also be able to verify this proof, and thus
accept the validity of the *link* throughout the network.
* | When the validity of the *link* has been accepted by forwarding nodes, these nodes will
remember the *link* , and it can subsequently be used by referring to a hash representing it.
* | As a part of the *link request* , a Diffie-Hellman key exchange takes place, that sets up an
efficiently encrypted tunnel between the two nodes, using elliptic curve cryptography. As such,
this mode of communication is preferred, even for situations when nodes can directly communicate,
when the amount of data to be exchanged numbers in the tens of packets.
* | When a *link* has been set up, it automatically provides message receipt functionality, through
the same *proof* mechanism discussed before, so the sending node can obtain verified confirmation
that the information reached the intended recipient.
In a moment, we will discuss the details of how this methodology is implemented, but lets first
recap what purposes this methodology serves. We first ensure that the node answering our request
is actually the one we want to communicate with, and not a malicious actor pretending to be so.
At the same time we establish an efficient encrypted channel. The setup of this is relatively cheap in
terms of bandwidth, so it can be used just for a short exchange, and then recreated as needed, which will
also rotate encryption keys. The link can also be kept alive for longer periods of time, if this is
more suitable to the application. The procedure also inserts the *link id* , a hash calculated from the link request packet, into the memory of forwarding nodes, which means that the communicating nodes can thereafter reach each other simply by referring to this *link id*.
The combined bandwidth cost of setting up a link is 3 packets totalling 240 bytes (more info in the
:ref:`Binary Packet Format<understanding-packetformat>` section). The amount of bandwidth used on keeping
a link open is practically negligible, at 0.62 bits per second. Even on a slow 1200 bits per second packet
radio channel, 100 concurrent links will still leave 95% channel capacity for actual data.
Link Establishment in Detail
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
After exploring the basics of the announce mechanism, finding a path through the network, and an overview
of the link establishment procedure, this section will go into greater detail about the Reticulum link
establishment process.
The *link* in Reticulum terminology should not be viewed as a direct node-to-node link on the
physical layer, but as an abstract channel, that can be open for any amount of time, and can span
an arbitrary number of hops, where information will be exchanged between two nodes.
* | When a node in the network wants to establish verified connectivity with another node, it * | When a node in the network wants to establish verified connectivity with another node, it
@@ -412,25 +457,25 @@ of hops, where information will be exchanged between two nodes.
considered as single public key for simplicity in this explanation.* considered as single public key for simplicity in this explanation.*
* | The *link request* is addressed to the destination hash of the desired destination, and * | The *link request* is addressed to the destination hash of the desired destination, and
contains the following data: The newly generated X25519 public key *LKi*. The contents contains the following data: The newly generated X25519 public key *LKi*.
are encrypted with the RSA public key of the destination and tramsitted over the network.
* | The broadcasted packet will be directed through the network according to the rules laid out * | The broadcasted packet will be directed through the network according to the rules laid out
previously. previously.
* | Any node that forwards the link request will store a *link id* in its *link table* , along with the * | Any node that forwards the link request will store a *link id* in its *link table* , along with the
amount of hops the packet had taken when received. The link id is a hash of the entire link amount of hops the packet had taken when received. The link id is a hash of the entire link
request packet. If the path is not *proven* within some set amount of time, the entry will be request packet. If the link request packet is not *proven* by the addressed destination within some
dropped from the *link table* again. set amount of time, the entry will be dropped from the *link table* again.
* | When the destination receives the link request packet, it will decrypt it and decide whether to * | When the destination receives the link request packet, it will decide whether to accept the request.
accept the request. If it is accepted, the destination will also generate a new X25519 private/public If it is accepted, the destination will also generate a new X25519 private/public key pair, and
key pair, and perform a Diffie Hellman Key Exchange, deriving a new symmetric key that will be used perform a Diffie Hellman Key Exchange, deriving a new symmetric key that will be used to encrypt the
to encrypt the channel, once it has been established. channel, once it has been established.
* | A *link proof* packet is now constructed and transmitted over the network. This packet is * | A *link proof* packet is now constructed and transmitted over the network. This packet is
addressed to the *link id* of the *link*. It contains the following data: The newly generated X25519 addressed to the *link id* of the *link*. It contains the following data: The newly generated X25519
public key *LKr* and an RSA-1024 signature of the *link id* and *LKr*. public key *LKr* and an Ed25519 signature of the *link id* and *LKr* made by the signing key of
the addressed destination.
* | By verifying this *link proof* packet, all nodes that originally transported the *link request* * | By verifying this *link proof* packet, all nodes that originally transported the *link request*
packet to the destination from the originator can now verify that the intended destination received packet to the destination from the originator can now verify that the intended destination received
@@ -556,6 +601,8 @@ the light of Reticulums goal of equal access, doing so would need to be the subj
investigation of the consequences first. investigation of the consequences first.
.. _understanding-packetformat:
Binary Packet Format Binary Packet Format
-------------------- --------------------
@@ -651,8 +698,8 @@ Binary Packet Format
wire size including all fields. wire size including all fields.
- Path Request : 33 bytes - Path Request : 33 bytes
- Announce : 323 bytes - Announce : 151 bytes
- Link Request : 141 bytes - Link Request : 77 bytes
- Link Proof : 205 bytes - Link Proof : 77 bytes
- Link RTT packet : 86 bytes - Link RTT packet : 86 bytes
- Link keepalive : 14 bytes - Link keepalive : 14 bytes
+16 -6
View File
@@ -2,7 +2,9 @@
What is Reticulum? What is Reticulum?
****************** ******************
Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth. Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more. Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth.
Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.
Reticulum is a complete networking stack, and does not use IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks. Reticulum is built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks. Reticulum is a complete networking stack, and does not use IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks. Reticulum is built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.
@@ -25,11 +27,11 @@ What does Reticulum Offer?
* Fully self-configuring multi-hop routing * Fully self-configuring multi-hop routing
* Asymmetric RSA encryption and signatures as basis for all communication * Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication
* Perfect Forward Secrecy on links with ephemereal Elliptic Curve Diffie-Hellman keys (on Curve25519) * Forward Secrecy with ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519
* Reticulum uses the Fernet specification for encryption on links and to group destinations * Reticulum uses the `Fernet <https://github.com/fernet/spec/blob/master/Spec.md>`_ specification for encryption
* AES-128 in CBC mode with PKCS7 padding * AES-128 in CBC mode with PKCS7 padding
@@ -37,11 +39,13 @@ What does Reticulum Offer?
* IVs are generated through os.urandom() * IVs are generated through os.urandom()
* Keys are ephemeral and derived from an ECDH key exchange on Curve25519
* Unforgeable packet delivery confirmations * Unforgeable packet delivery confirmations
* A variety of supported interface types * A variety of supported interface types
* An intuitive and easy-to-use API * An intuitive and developer-friendly API
* Reliable and efficient transfer of arbritrary amounts of data * Reliable and efficient transfer of arbritrary amounts of data
@@ -51,10 +55,16 @@ What does Reticulum Offer?
* The API is very easy to use, and provides transfer progress * The API is very easy to use, and provides transfer progress
* Efficient link establishment
* Total bandwidth cost of setting up a link is only 3 packets, totalling 240 bytes
* Low cost of keeping links open at only 0.62 bits per second
Where can Reticulum be Used? Where can Reticulum be Used?
============================ ============================
On practically any hardware that can support at least a half-duplex channel Over practically any medium that can support at least a half-duplex channel
with 1.000 bits per second throughput, and an MTU of 500 bytes. Data radios, with 1.000 bits per second throughput, and an MTU of 500 bytes. Data radios,
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes, modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
ad-hoc WiFi, free-space optical links and similar systems are all examples ad-hoc WiFi, free-space optical links and similar systems are all examples
+1 -1
View File
@@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = { var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.2.0 beta', VERSION: '0.2.2 beta',
LANGUAGE: 'None', LANGUAGE: 'None',
COLLAPSE_INDEX: false, COLLAPSE_INDEX: false,
BUILDER: 'html', BUILDER: 'html',
+651 -37
View File
@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Examples &#8212; Reticulum Network Stack 0.2.0 beta documentation</title> <title>Examples &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" /> <link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -16,8 +16,7 @@
<link rel="index" title="Index" href="genindex.html" /> <link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" /> <link rel="search" title="Search" href="search.html" />
<link rel="next" title="API Reference" href="reference.html" /> <link rel="prev" title="API Reference" href="reference.html" />
<link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
</head><body> </head><body>
<div class="related" role="navigation" aria-label="related navigation"> <div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3> <h3>Navigation</h3>
@@ -27,11 +26,8 @@
accesskey="I">index</a></li> accesskey="I">index</a></li>
<li class="right" > <li class="right" >
<a href="reference.html" title="API Reference" <a href="reference.html" title="API Reference"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="gettingstartedfast.html" title="Getting Started Fast"
accesskey="P">previous</a> |</li> accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Examples</a></li> <li class="nav-item nav-item-this"><a href="">Examples</a></li>
</ul> </ul>
</div> </div>
@@ -380,7 +376,7 @@ over the network.</p>
<span class="c1"># We specify a callback that will get called every time</span> <span class="c1"># We specify a callback that will get called every time</span>
<span class="c1"># the destination receives data.</span> <span class="c1"># the destination receives data.</span>
<span class="n">broadcast_destination</span><span class="o">.</span><span class="n">packet_callback</span><span class="p">(</span><span class="n">packet_callback</span><span class="p">)</span> <span class="n">broadcast_destination</span><span class="o">.</span><span class="n">set_packet_callback</span><span class="p">(</span><span class="n">packet_callback</span><span class="p">)</span>
<span class="c1"># Everything&#39;s ready!</span> <span class="c1"># Everything&#39;s ready!</span>
<span class="c1"># Let&#39;s hand over control to the main loop</span> <span class="c1"># Let&#39;s hand over control to the main loop</span>
@@ -522,7 +518,7 @@ the Packet interface.</p>
<span class="c1"># Tell the destination which function in our program to</span> <span class="c1"># Tell the destination which function in our program to</span>
<span class="c1"># run when a packet is received. We do this so we can</span> <span class="c1"># run when a packet is received. We do this so we can</span>
<span class="c1"># print a log message when the server receives a request</span> <span class="c1"># print a log message when the server receives a request</span>
<span class="n">echo_destination</span><span class="o">.</span><span class="n">packet_callback</span><span class="p">(</span><span class="n">server_callback</span><span class="p">)</span> <span class="n">echo_destination</span><span class="o">.</span><span class="n">set_packet_callback</span><span class="p">(</span><span class="n">server_callback</span><span class="p">)</span>
<span class="c1"># Everything&#39;s ready!</span> <span class="c1"># Everything&#39;s ready!</span>
<span class="c1"># Let&#39;s Wait for client requests or user input</span> <span class="c1"># Let&#39;s Wait for client requests or user input</span>
@@ -640,12 +636,12 @@ the Packet interface.</p>
<span class="c1"># the packet times out.</span> <span class="c1"># the packet times out.</span>
<span class="k">if</span> <span class="n">timeout</span> <span class="o">!=</span> <span class="kc">None</span><span class="p">:</span> <span class="k">if</span> <span class="n">timeout</span> <span class="o">!=</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">packet_receipt</span><span class="o">.</span><span class="n">set_timeout</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span> <span class="n">packet_receipt</span><span class="o">.</span><span class="n">set_timeout</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span>
<span class="n">packet_receipt</span><span class="o">.</span><span class="n">timeout_callback</span><span class="p">(</span><span class="n">packet_timed_out</span><span class="p">)</span> <span class="n">packet_receipt</span><span class="o">.</span><span class="n">set_timeout_callback</span><span class="p">(</span><span class="n">packet_timed_out</span><span class="p">)</span>
<span class="c1"># We can then set a delivery callback on the receipt.</span> <span class="c1"># We can then set a delivery callback on the receipt.</span>
<span class="c1"># This will get automatically called when a proof for</span> <span class="c1"># This will get automatically called when a proof for</span>
<span class="c1"># this specific packet is received from the destination.</span> <span class="c1"># this specific packet is received from the destination.</span>
<span class="n">packet_receipt</span><span class="o">.</span><span class="n">delivery_callback</span><span class="p">(</span><span class="n">packet_delivered</span><span class="p">)</span> <span class="n">packet_receipt</span><span class="o">.</span><span class="n">set_delivery_callback</span><span class="p">(</span><span class="n">packet_delivered</span><span class="p">)</span>
<span class="c1"># Tell the user that the echo request was sent</span> <span class="c1"># Tell the user that the echo request was sent</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Sent echo request to &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">request_destination</span><span class="o">.</span><span class="n">hash</span><span class="p">))</span> <span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Sent echo request to &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">request_destination</span><span class="o">.</span><span class="n">hash</span><span class="p">))</span>
@@ -659,7 +655,7 @@ the Packet interface.</p>
<span class="c1"># receives a proof packet.</span> <span class="c1"># receives a proof packet.</span>
<span class="k">def</span> <span class="nf">packet_delivered</span><span class="p">(</span><span class="n">receipt</span><span class="p">):</span> <span class="k">def</span> <span class="nf">packet_delivered</span><span class="p">(</span><span class="n">receipt</span><span class="p">):</span>
<span class="k">if</span> <span class="n">receipt</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">PacketReceipt</span><span class="o">.</span><span class="n">DELIVERED</span><span class="p">:</span> <span class="k">if</span> <span class="n">receipt</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">PacketReceipt</span><span class="o">.</span><span class="n">DELIVERED</span><span class="p">:</span>
<span class="n">rtt</span> <span class="o">=</span> <span class="n">receipt</span><span class="o">.</span><span class="n">rtt</span><span class="p">()</span> <span class="n">rtt</span> <span class="o">=</span> <span class="n">receipt</span><span class="o">.</span><span class="n">get_rtt</span><span class="p">()</span>
<span class="k">if</span> <span class="p">(</span><span class="n">rtt</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">):</span> <span class="k">if</span> <span class="p">(</span><span class="n">rtt</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">):</span>
<span class="n">rtt</span> <span class="o">=</span> <span class="nb">round</span><span class="p">(</span><span class="n">rtt</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="n">rtt</span> <span class="o">=</span> <span class="nb">round</span><span class="p">(</span><span class="n">rtt</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="n">rttstring</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">rtt</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot; seconds&quot;</span> <span class="n">rttstring</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">rtt</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot; seconds&quot;</span>
@@ -803,7 +799,7 @@ destination, and passing traffic back and forth over the link.</p>
<span class="c1"># We configure a function that will get called every time</span> <span class="c1"># We configure a function that will get called every time</span>
<span class="c1"># a new client creates a link to this destination.</span> <span class="c1"># a new client creates a link to this destination.</span>
<span class="n">server_destination</span><span class="o">.</span><span class="n">link_established_callback</span><span class="p">(</span><span class="n">client_connected</span><span class="p">)</span> <span class="n">server_destination</span><span class="o">.</span><span class="n">set_link_established_callback</span><span class="p">(</span><span class="n">client_connected</span><span class="p">)</span>
<span class="c1"># Everything&#39;s ready!</span> <span class="c1"># Everything&#39;s ready!</span>
<span class="c1"># Let&#39;s Wait for client requests or user input</span> <span class="c1"># Let&#39;s Wait for client requests or user input</span>
@@ -835,8 +831,8 @@ destination, and passing traffic back and forth over the link.</p>
<span class="k">global</span> <span class="n">latest_client_link</span> <span class="k">global</span> <span class="n">latest_client_link</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client connected&quot;</span><span class="p">)</span> <span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client connected&quot;</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">link_closed_callback</span><span class="p">(</span><span class="n">client_disconnected</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_link_closed_callback</span><span class="p">(</span><span class="n">client_disconnected</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">packet_callback</span><span class="p">(</span><span class="n">server_packet_received</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_packet_callback</span><span class="p">(</span><span class="n">server_packet_received</span><span class="p">)</span>
<span class="n">latest_client_link</span> <span class="o">=</span> <span class="n">link</span> <span class="n">latest_client_link</span> <span class="o">=</span> <span class="n">link</span>
<span class="k">def</span> <span class="nf">client_disconnected</span><span class="p">(</span><span class="n">link</span><span class="p">):</span> <span class="k">def</span> <span class="nf">client_disconnected</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
@@ -908,12 +904,12 @@ destination, and passing traffic back and forth over the link.</p>
<span class="c1"># We set a callback that will get executed</span> <span class="c1"># We set a callback that will get executed</span>
<span class="c1"># every time a packet is received over the</span> <span class="c1"># every time a packet is received over the</span>
<span class="c1"># link</span> <span class="c1"># link</span>
<span class="n">link</span><span class="o">.</span><span class="n">packet_callback</span><span class="p">(</span><span class="n">client_packet_received</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_packet_callback</span><span class="p">(</span><span class="n">client_packet_received</span><span class="p">)</span>
<span class="c1"># We&#39;ll also set up functions to inform the</span> <span class="c1"># We&#39;ll also set up functions to inform the</span>
<span class="c1"># user when the link is established or closed</span> <span class="c1"># user when the link is established or closed</span>
<span class="n">link</span><span class="o">.</span><span class="n">link_established_callback</span><span class="p">(</span><span class="n">link_established</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_link_established_callback</span><span class="p">(</span><span class="n">link_established</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">link_closed_callback</span><span class="p">(</span><span class="n">link_closed</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_link_closed_callback</span><span class="p">(</span><span class="n">link_closed</span><span class="p">)</span>
<span class="c1"># Everything is set up, so let&#39;s enter a loop</span> <span class="c1"># Everything is set up, so let&#39;s enter a loop</span>
<span class="c1"># for the user to interact with the example</span> <span class="c1"># for the user to interact with the example</span>
@@ -940,8 +936,18 @@ destination, and passing traffic back and forth over the link.</p>
<span class="c1"># If not, send the entered text over the link</span> <span class="c1"># If not, send the entered text over the link</span>
<span class="k">if</span> <span class="n">text</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span><span class="p">:</span> <span class="k">if</span> <span class="n">text</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span><span class="p">:</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">text</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span> <span class="n">data</span> <span class="o">=</span> <span class="n">text</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">MDU</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Packet</span><span class="p">(</span><span class="n">server_link</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span><span class="o">.</span><span class="n">send</span><span class="p">()</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Packet</span><span class="p">(</span><span class="n">server_link</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span><span class="o">.</span><span class="n">send</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
<span class="s2">&quot;Cannot send this packet, the data size of &quot;</span><span class="o">+</span>
<span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">))</span><span class="o">+</span><span class="s2">&quot; bytes exceeds the link packet MDU of &quot;</span><span class="o">+</span>
<span class="nb">str</span><span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">MDU</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot; bytes&quot;</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">LOG_ERROR</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Error while sending data over the link: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">True</span> <span class="n">should_quit</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">server_link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span> <span class="n">server_link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span>
@@ -1038,6 +1044,614 @@ destination, and passing traffic back and forth over the link.</p>
</div> </div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py</a>.</p> <p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py</a>.</p>
</div> </div>
<div class="section" id="example-identify">
<span id="identification"></span><h2>Identification<a class="headerlink" href="#example-identify" title="Permalink to this headline"></a></h2>
<p>The <em>Identify</em> example explores identifying an intiator of a link, once
the link has been established.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1">##########################################################</span>
<span class="c1"># This RNS example demonstrates how to set up a link to #</span>
<span class="c1"># a destination, and identify the initiator to it&#39;s peer #</span>
<span class="c1">##########################################################</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">RNS</span>
<span class="c1"># Let&#39;s define an app name. We&#39;ll use this for all</span>
<span class="c1"># destinations we create. Since this echo example</span>
<span class="c1"># is part of a range of example utilities, we&#39;ll put</span>
<span class="c1"># them all within the app namespace &quot;example_utilities&quot;</span>
<span class="n">APP_NAME</span> <span class="o">=</span> <span class="s2">&quot;example_utilities&quot;</span>
<span class="c1">##########################################################</span>
<span class="c1">#### Server Part #########################################</span>
<span class="c1">##########################################################</span>
<span class="c1"># A reference to the latest client link that connected</span>
<span class="n">latest_client_link</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># This initialisation is executed when the users chooses</span>
<span class="c1"># to run as a server</span>
<span class="k">def</span> <span class="nf">server</span><span class="p">(</span><span class="n">configpath</span><span class="p">):</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
<span class="c1"># Randomly create a new identity for our link example</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
<span class="c1"># We create a destination that clients can connect to. We</span>
<span class="c1"># want clients to create links to this destination, so we</span>
<span class="c1"># need to create a &quot;single&quot; destination type.</span>
<span class="n">server_destination</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
<span class="n">server_identity</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">IN</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">SINGLE</span><span class="p">,</span>
<span class="n">APP_NAME</span><span class="p">,</span>
<span class="s2">&quot;identifyexample&quot;</span>
<span class="p">)</span>
<span class="c1"># We configure a function that will get called every time</span>
<span class="c1"># a new client creates a link to this destination.</span>
<span class="n">server_destination</span><span class="o">.</span><span class="n">set_link_established_callback</span><span class="p">(</span><span class="n">client_connected</span><span class="p">)</span>
<span class="c1"># Everything&#39;s ready!</span>
<span class="c1"># Let&#39;s Wait for client requests or user input</span>
<span class="n">server_loop</span><span class="p">(</span><span class="n">server_destination</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">server_loop</span><span class="p">(</span><span class="n">destination</span><span class="p">):</span>
<span class="c1"># Let the user know that everything is ready</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
<span class="s2">&quot;Link identification example &quot;</span><span class="o">+</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">destination</span><span class="o">.</span><span class="n">hash</span><span class="p">)</span><span class="o">+</span>
<span class="s2">&quot; running, waiting for a connection.&quot;</span>
<span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Hit enter to manually send an announce (Ctrl-C to quit)&quot;</span><span class="p">)</span>
<span class="c1"># We enter a loop that runs until the users exits.</span>
<span class="c1"># If the user hits enter, we will announce our server</span>
<span class="c1"># destination on the network, which will let clients</span>
<span class="c1"># know how to create messages directed towards it.</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">entered</span> <span class="o">=</span> <span class="nb">input</span><span class="p">()</span>
<span class="n">destination</span><span class="o">.</span><span class="n">announce</span><span class="p">()</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Sent announce from &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">destination</span><span class="o">.</span><span class="n">hash</span><span class="p">))</span>
<span class="c1"># When a client establishes a link to our server</span>
<span class="c1"># destination, this function will be called with</span>
<span class="c1"># a reference to the link.</span>
<span class="k">def</span> <span class="nf">client_connected</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="k">global</span> <span class="n">latest_client_link</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client connected&quot;</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_link_closed_callback</span><span class="p">(</span><span class="n">client_disconnected</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_packet_callback</span><span class="p">(</span><span class="n">server_packet_received</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_remote_identified_callback</span><span class="p">(</span><span class="n">remote_identified</span><span class="p">)</span>
<span class="n">latest_client_link</span> <span class="o">=</span> <span class="n">link</span>
<span class="k">def</span> <span class="nf">client_disconnected</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client disconnected&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">remote_identified</span><span class="p">(</span><span class="n">identity</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Remote identified as: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">identity</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">server_packet_received</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">packet</span><span class="p">):</span>
<span class="k">global</span> <span class="n">latest_client_link</span>
<span class="c1"># Get the originating identity for display</span>
<span class="n">remote_peer</span> <span class="o">=</span> <span class="s2">&quot;unidentified peer&quot;</span>
<span class="k">if</span> <span class="n">packet</span><span class="o">.</span><span class="n">link</span><span class="o">.</span><span class="n">get_remote_identity</span><span class="p">()</span> <span class="o">!=</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">remote_peer</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">packet</span><span class="o">.</span><span class="n">link</span><span class="o">.</span><span class="n">get_remote_identity</span><span class="p">())</span>
<span class="c1"># When data is received over any active link,</span>
<span class="c1"># it will all be directed to the last client</span>
<span class="c1"># that connected.</span>
<span class="n">text</span> <span class="o">=</span> <span class="n">message</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Received data from &quot;</span><span class="o">+</span><span class="n">remote_peer</span><span class="o">+</span><span class="s2">&quot;: &quot;</span><span class="o">+</span><span class="n">text</span><span class="p">)</span>
<span class="n">reply_text</span> <span class="o">=</span> <span class="s2">&quot;I received </span><span class="se">\&quot;</span><span class="s2">&quot;</span><span class="o">+</span><span class="n">text</span><span class="o">+</span><span class="s2">&quot;</span><span class="se">\&quot;</span><span class="s2"> over the link from &quot;</span><span class="o">+</span><span class="n">remote_peer</span>
<span class="n">reply_data</span> <span class="o">=</span> <span class="n">reply_text</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Packet</span><span class="p">(</span><span class="n">latest_client_link</span><span class="p">,</span> <span class="n">reply_data</span><span class="p">)</span><span class="o">.</span><span class="n">send</span><span class="p">()</span>
<span class="c1">##########################################################</span>
<span class="c1">#### Client Part #########################################</span>
<span class="c1">##########################################################</span>
<span class="c1"># A reference to the server link</span>
<span class="n">server_link</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># A reference to the client identity</span>
<span class="n">client_identity</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># This initialisation is executed when the users chooses</span>
<span class="c1"># to run as a client</span>
<span class="k">def</span> <span class="nf">client</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">,</span> <span class="n">configpath</span><span class="p">):</span>
<span class="k">global</span> <span class="n">client_identity</span>
<span class="c1"># We need a binary representation of the destination</span>
<span class="c1"># hash that was entered on the command line</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">20</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Destination length is invalid, must be 20 hexadecimal characters (10 bytes)&quot;</span><span class="p">)</span>
<span class="n">destination_hash</span> <span class="o">=</span> <span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Invalid destination entered. Check your input!</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
<span class="c1"># Create a new client identity</span>
<span class="n">client_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
<span class="s2">&quot;Client created new identity &quot;</span><span class="o">+</span>
<span class="nb">str</span><span class="p">(</span><span class="n">client_identity</span><span class="p">)</span>
<span class="p">)</span>
<span class="c1"># Check if we know a path to the destination</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Transport</span><span class="o">.</span><span class="n">has_path</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Destination is not yet known. Requesting path and waiting for announce to arrive...&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Transport</span><span class="o">.</span><span class="n">request_path</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">)</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Transport</span><span class="o">.</span><span class="n">has_path</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">):</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
<span class="c1"># Recall the server identity</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="o">.</span><span class="n">recall</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">)</span>
<span class="c1"># Inform the user that we&#39;ll begin connecting</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Establishing link with server...&quot;</span><span class="p">)</span>
<span class="c1"># When the server identity is known, we set</span>
<span class="c1"># up a destination</span>
<span class="n">server_destination</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
<span class="n">server_identity</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">SINGLE</span><span class="p">,</span>
<span class="n">APP_NAME</span><span class="p">,</span>
<span class="s2">&quot;identifyexample&quot;</span>
<span class="p">)</span>
<span class="c1"># And create a link</span>
<span class="n">link</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="p">(</span><span class="n">server_destination</span><span class="p">)</span>
<span class="c1"># We set a callback that will get executed</span>
<span class="c1"># every time a packet is received over the</span>
<span class="c1"># link</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_packet_callback</span><span class="p">(</span><span class="n">client_packet_received</span><span class="p">)</span>
<span class="c1"># We&#39;ll also set up functions to inform the</span>
<span class="c1"># user when the link is established or closed</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_link_established_callback</span><span class="p">(</span><span class="n">link_established</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_link_closed_callback</span><span class="p">(</span><span class="n">link_closed</span><span class="p">)</span>
<span class="c1"># Everything is set up, so let&#39;s enter a loop</span>
<span class="c1"># for the user to interact with the example</span>
<span class="n">client_loop</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">client_loop</span><span class="p">():</span>
<span class="k">global</span> <span class="n">server_link</span>
<span class="c1"># Wait for the link to become active</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">server_link</span><span class="p">:</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">should_quit</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&gt; &quot;</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&quot; &quot;</span><span class="p">)</span>
<span class="n">text</span> <span class="o">=</span> <span class="nb">input</span><span class="p">()</span>
<span class="c1"># Check if we should quit the example</span>
<span class="k">if</span> <span class="n">text</span> <span class="o">==</span> <span class="s2">&quot;quit&quot;</span> <span class="ow">or</span> <span class="n">text</span> <span class="o">==</span> <span class="s2">&quot;q&quot;</span> <span class="ow">or</span> <span class="n">text</span> <span class="o">==</span> <span class="s2">&quot;exit&quot;</span><span class="p">:</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">server_link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span>
<span class="c1"># If not, send the entered text over the link</span>
<span class="k">if</span> <span class="n">text</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span><span class="p">:</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">text</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">MDU</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Packet</span><span class="p">(</span><span class="n">server_link</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span><span class="o">.</span><span class="n">send</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
<span class="s2">&quot;Cannot send this packet, the data size of &quot;</span><span class="o">+</span>
<span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">))</span><span class="o">+</span><span class="s2">&quot; bytes exceeds the link packet MDU of &quot;</span><span class="o">+</span>
<span class="nb">str</span><span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">MDU</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot; bytes&quot;</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">LOG_ERROR</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Error while sending data over the link: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">server_link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span>
<span class="c1"># This function is called when a link</span>
<span class="c1"># has been established with the server</span>
<span class="k">def</span> <span class="nf">link_established</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="c1"># We store a reference to the link</span>
<span class="c1"># instance for later use</span>
<span class="k">global</span> <span class="n">server_link</span><span class="p">,</span> <span class="n">client_identity</span>
<span class="n">server_link</span> <span class="o">=</span> <span class="n">link</span>
<span class="c1"># Inform the user that the server is</span>
<span class="c1"># connected</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link established with server, identifying to remote peer...&quot;</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">identify</span><span class="p">(</span><span class="n">client_identity</span><span class="p">)</span>
<span class="c1"># When a link is closed, we&#39;ll inform the</span>
<span class="c1"># user, and exit the program</span>
<span class="k">def</span> <span class="nf">link_closed</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="k">if</span> <span class="n">link</span><span class="o">.</span><span class="n">teardown_reason</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">TIMEOUT</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;The link timed out, exiting now&quot;</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">link</span><span class="o">.</span><span class="n">teardown_reason</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">DESTINATION_CLOSED</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;The link was closed by the server, exiting now&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link closed, exiting now&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">exit_handler</span><span class="p">()</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># When a packet is received over the link, we</span>
<span class="c1"># simply print out the data.</span>
<span class="k">def</span> <span class="nf">client_packet_received</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">packet</span><span class="p">):</span>
<span class="n">text</span> <span class="o">=</span> <span class="n">message</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Received data on the link: &quot;</span><span class="o">+</span><span class="n">text</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&gt; &quot;</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&quot; &quot;</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
<span class="c1">##########################################################</span>
<span class="c1">#### Program Startup #####################################</span>
<span class="c1">##########################################################</span>
<span class="c1"># This part of the program runs at startup,</span>
<span class="c1"># and parses input of from the user, and then</span>
<span class="c1"># starts up the desired program mode.</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="o">.</span><span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&quot;Simple link example&quot;</span><span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s2">&quot;-s&quot;</span><span class="p">,</span>
<span class="s2">&quot;--server&quot;</span><span class="p">,</span>
<span class="n">action</span><span class="o">=</span><span class="s2">&quot;store_true&quot;</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;wait for incoming link requests from clients&quot;</span>
<span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s2">&quot;--config&quot;</span><span class="p">,</span>
<span class="n">action</span><span class="o">=</span><span class="s2">&quot;store&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;path to alternative Reticulum config directory&quot;</span><span class="p">,</span>
<span class="nb">type</span><span class="o">=</span><span class="nb">str</span>
<span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s2">&quot;destination&quot;</span><span class="p">,</span>
<span class="n">nargs</span><span class="o">=</span><span class="s2">&quot;?&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;hexadecimal hash of the server destination&quot;</span><span class="p">,</span>
<span class="nb">type</span><span class="o">=</span><span class="nb">str</span>
<span class="p">)</span>
<span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span>
<span class="k">if</span> <span class="n">args</span><span class="o">.</span><span class="n">config</span><span class="p">:</span>
<span class="n">configarg</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">config</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">configarg</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">args</span><span class="o">.</span><span class="n">server</span><span class="p">:</span>
<span class="n">server</span><span class="p">(</span><span class="n">configarg</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">destination</span> <span class="o">==</span> <span class="kc">None</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">print_help</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">client</span><span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">destination</span><span class="p">,</span> <span class="n">configarg</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Identify.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Identify.py</a>.</p>
</div>
<div class="section" id="requests-responses">
<span id="example-request"></span><h2>Requests &amp; Responses<a class="headerlink" href="#requests-responses" title="Permalink to this headline"></a></h2>
<p>The <em>Request</em> example explores sendig requests and receiving responses.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1">##########################################################</span>
<span class="c1"># This RNS example demonstrates how to set perform #</span>
<span class="c1"># requests and receive responses over a link. #</span>
<span class="c1">##########################################################</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">RNS</span>
<span class="c1"># Let&#39;s define an app name. We&#39;ll use this for all</span>
<span class="c1"># destinations we create. Since this echo example</span>
<span class="c1"># is part of a range of example utilities, we&#39;ll put</span>
<span class="c1"># them all within the app namespace &quot;example_utilities&quot;</span>
<span class="n">APP_NAME</span> <span class="o">=</span> <span class="s2">&quot;example_utilities&quot;</span>
<span class="c1">##########################################################</span>
<span class="c1">#### Server Part #########################################</span>
<span class="c1">##########################################################</span>
<span class="c1"># A reference to the latest client link that connected</span>
<span class="n">latest_client_link</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">random_text_generator</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">request_id</span><span class="p">,</span> <span class="n">remote_identity</span><span class="p">,</span> <span class="n">requested_at</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Generating response to request &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">request_id</span><span class="p">))</span>
<span class="n">texts</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;They looked up&quot;</span><span class="p">,</span> <span class="s2">&quot;On each full moon&quot;</span><span class="p">,</span> <span class="s2">&quot;Becky was upset&quot;</span><span class="p">,</span> <span class="s2">&quot;Ill stay away from it&quot;</span><span class="p">,</span> <span class="s2">&quot;The pet shop stocks everything&quot;</span><span class="p">]</span>
<span class="k">return</span> <span class="n">texts</span><span class="p">[</span><span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">texts</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)]</span>
<span class="c1"># This initialisation is executed when the users chooses</span>
<span class="c1"># to run as a server</span>
<span class="k">def</span> <span class="nf">server</span><span class="p">(</span><span class="n">configpath</span><span class="p">):</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
<span class="c1"># Randomly create a new identity for our link example</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
<span class="c1"># We create a destination that clients can connect to. We</span>
<span class="c1"># want clients to create links to this destination, so we</span>
<span class="c1"># need to create a &quot;single&quot; destination type.</span>
<span class="n">server_destination</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
<span class="n">server_identity</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">IN</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">SINGLE</span><span class="p">,</span>
<span class="n">APP_NAME</span><span class="p">,</span>
<span class="s2">&quot;requestexample&quot;</span>
<span class="p">)</span>
<span class="c1"># We configure a function that will get called every time</span>
<span class="c1"># a new client creates a link to this destination.</span>
<span class="n">server_destination</span><span class="o">.</span><span class="n">set_link_established_callback</span><span class="p">(</span><span class="n">client_connected</span><span class="p">)</span>
<span class="c1"># We register a request handler for handling incoming</span>
<span class="c1"># requests over any established links.</span>
<span class="n">server_destination</span><span class="o">.</span><span class="n">register_request_handler</span><span class="p">(</span>
<span class="s2">&quot;/random/text&quot;</span><span class="p">,</span>
<span class="n">response_generator</span> <span class="o">=</span> <span class="n">random_text_generator</span><span class="p">,</span>
<span class="n">allow</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">ALLOW_ALL</span>
<span class="p">)</span>
<span class="c1"># Everything&#39;s ready!</span>
<span class="c1"># Let&#39;s Wait for client requests or user input</span>
<span class="n">server_loop</span><span class="p">(</span><span class="n">server_destination</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">server_loop</span><span class="p">(</span><span class="n">destination</span><span class="p">):</span>
<span class="c1"># Let the user know that everything is ready</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
<span class="s2">&quot;Request example &quot;</span><span class="o">+</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">destination</span><span class="o">.</span><span class="n">hash</span><span class="p">)</span><span class="o">+</span>
<span class="s2">&quot; running, waiting for a connection.&quot;</span>
<span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Hit enter to manually send an announce (Ctrl-C to quit)&quot;</span><span class="p">)</span>
<span class="c1"># We enter a loop that runs until the users exits.</span>
<span class="c1"># If the user hits enter, we will announce our server</span>
<span class="c1"># destination on the network, which will let clients</span>
<span class="c1"># know how to create messages directed towards it.</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">entered</span> <span class="o">=</span> <span class="nb">input</span><span class="p">()</span>
<span class="n">destination</span><span class="o">.</span><span class="n">announce</span><span class="p">()</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Sent announce from &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">destination</span><span class="o">.</span><span class="n">hash</span><span class="p">))</span>
<span class="c1"># When a client establishes a link to our server</span>
<span class="c1"># destination, this function will be called with</span>
<span class="c1"># a reference to the link.</span>
<span class="k">def</span> <span class="nf">client_connected</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="k">global</span> <span class="n">latest_client_link</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client connected&quot;</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_link_closed_callback</span><span class="p">(</span><span class="n">client_disconnected</span><span class="p">)</span>
<span class="n">latest_client_link</span> <span class="o">=</span> <span class="n">link</span>
<span class="k">def</span> <span class="nf">client_disconnected</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client disconnected&quot;</span><span class="p">)</span>
<span class="c1">##########################################################</span>
<span class="c1">#### Client Part #########################################</span>
<span class="c1">##########################################################</span>
<span class="c1"># A reference to the server link</span>
<span class="n">server_link</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># This initialisation is executed when the users chooses</span>
<span class="c1"># to run as a client</span>
<span class="k">def</span> <span class="nf">client</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">,</span> <span class="n">configpath</span><span class="p">):</span>
<span class="c1"># We need a binary representation of the destination</span>
<span class="c1"># hash that was entered on the command line</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">20</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Destination length is invalid, must be 20 hexadecimal characters (10 bytes)&quot;</span><span class="p">)</span>
<span class="n">destination_hash</span> <span class="o">=</span> <span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Invalid destination entered. Check your input!</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
<span class="c1"># Check if we know a path to the destination</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Transport</span><span class="o">.</span><span class="n">has_path</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Destination is not yet known. Requesting path and waiting for announce to arrive...&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Transport</span><span class="o">.</span><span class="n">request_path</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">)</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Transport</span><span class="o">.</span><span class="n">has_path</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">):</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
<span class="c1"># Recall the server identity</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="o">.</span><span class="n">recall</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">)</span>
<span class="c1"># Inform the user that we&#39;ll begin connecting</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Establishing link with server...&quot;</span><span class="p">)</span>
<span class="c1"># When the server identity is known, we set</span>
<span class="c1"># up a destination</span>
<span class="n">server_destination</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
<span class="n">server_identity</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">SINGLE</span><span class="p">,</span>
<span class="n">APP_NAME</span><span class="p">,</span>
<span class="s2">&quot;requestexample&quot;</span>
<span class="p">)</span>
<span class="c1"># And create a link</span>
<span class="n">link</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="p">(</span><span class="n">server_destination</span><span class="p">)</span>
<span class="c1"># We&#39;ll set up functions to inform the</span>
<span class="c1"># user when the link is established or closed</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_link_established_callback</span><span class="p">(</span><span class="n">link_established</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_link_closed_callback</span><span class="p">(</span><span class="n">link_closed</span><span class="p">)</span>
<span class="c1"># Everything is set up, so let&#39;s enter a loop</span>
<span class="c1"># for the user to interact with the example</span>
<span class="n">client_loop</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">client_loop</span><span class="p">():</span>
<span class="k">global</span> <span class="n">server_link</span>
<span class="c1"># Wait for the link to become active</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">server_link</span><span class="p">:</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">should_quit</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&gt; &quot;</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&quot; &quot;</span><span class="p">)</span>
<span class="n">text</span> <span class="o">=</span> <span class="nb">input</span><span class="p">()</span>
<span class="c1"># Check if we should quit the example</span>
<span class="k">if</span> <span class="n">text</span> <span class="o">==</span> <span class="s2">&quot;quit&quot;</span> <span class="ow">or</span> <span class="n">text</span> <span class="o">==</span> <span class="s2">&quot;q&quot;</span> <span class="ow">or</span> <span class="n">text</span> <span class="o">==</span> <span class="s2">&quot;exit&quot;</span><span class="p">:</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">server_link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">server_link</span><span class="o">.</span><span class="n">request</span><span class="p">(</span>
<span class="s2">&quot;/random/text&quot;</span><span class="p">,</span>
<span class="n">data</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">response_callback</span> <span class="o">=</span> <span class="n">got_response</span><span class="p">,</span>
<span class="n">failed_callback</span> <span class="o">=</span> <span class="n">request_failed</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Error while sending request over the link: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">server_link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">got_response</span><span class="p">(</span><span class="n">request_receipt</span><span class="p">):</span>
<span class="n">request_id</span> <span class="o">=</span> <span class="n">request_receipt</span><span class="o">.</span><span class="n">request_id</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">request_receipt</span><span class="o">.</span><span class="n">response</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Got response for request &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">request_id</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot;: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">response</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">request_received</span><span class="p">(</span><span class="n">request_receipt</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;The request &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">request_receipt</span><span class="o">.</span><span class="n">request_id</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot; was received by the remote peer.&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">request_failed</span><span class="p">(</span><span class="n">request_receipt</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;The request &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">request_receipt</span><span class="o">.</span><span class="n">request_id</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot; failed.&quot;</span><span class="p">)</span>
<span class="c1"># This function is called when a link</span>
<span class="c1"># has been established with the server</span>
<span class="k">def</span> <span class="nf">link_established</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="c1"># We store a reference to the link</span>
<span class="c1"># instance for later use</span>
<span class="k">global</span> <span class="n">server_link</span>
<span class="n">server_link</span> <span class="o">=</span> <span class="n">link</span>
<span class="c1"># Inform the user that the server is</span>
<span class="c1"># connected</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link established with server, hit enter to perform a request, or type in </span><span class="se">\&quot;</span><span class="s2">quit</span><span class="se">\&quot;</span><span class="s2"> to quit&quot;</span><span class="p">)</span>
<span class="c1"># When a link is closed, we&#39;ll inform the</span>
<span class="c1"># user, and exit the program</span>
<span class="k">def</span> <span class="nf">link_closed</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="k">if</span> <span class="n">link</span><span class="o">.</span><span class="n">teardown_reason</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">TIMEOUT</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;The link timed out, exiting now&quot;</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">link</span><span class="o">.</span><span class="n">teardown_reason</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">DESTINATION_CLOSED</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;The link was closed by the server, exiting now&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link closed, exiting now&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">exit_handler</span><span class="p">()</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1">##########################################################</span>
<span class="c1">#### Program Startup #####################################</span>
<span class="c1">##########################################################</span>
<span class="c1"># This part of the program runs at startup,</span>
<span class="c1"># and parses input of from the user, and then</span>
<span class="c1"># starts up the desired program mode.</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="o">.</span><span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&quot;Simple request/response example&quot;</span><span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s2">&quot;-s&quot;</span><span class="p">,</span>
<span class="s2">&quot;--server&quot;</span><span class="p">,</span>
<span class="n">action</span><span class="o">=</span><span class="s2">&quot;store_true&quot;</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;wait for incoming requests from clients&quot;</span>
<span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s2">&quot;--config&quot;</span><span class="p">,</span>
<span class="n">action</span><span class="o">=</span><span class="s2">&quot;store&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;path to alternative Reticulum config directory&quot;</span><span class="p">,</span>
<span class="nb">type</span><span class="o">=</span><span class="nb">str</span>
<span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s2">&quot;destination&quot;</span><span class="p">,</span>
<span class="n">nargs</span><span class="o">=</span><span class="s2">&quot;?&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;hexadecimal hash of the server destination&quot;</span><span class="p">,</span>
<span class="nb">type</span><span class="o">=</span><span class="nb">str</span>
<span class="p">)</span>
<span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span>
<span class="k">if</span> <span class="n">args</span><span class="o">.</span><span class="n">config</span><span class="p">:</span>
<span class="n">configarg</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">config</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">configarg</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">args</span><span class="o">.</span><span class="n">server</span><span class="p">:</span>
<span class="n">server</span><span class="p">(</span><span class="n">configarg</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">destination</span> <span class="o">==</span> <span class="kc">None</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">print_help</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">client</span><span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">destination</span><span class="p">,</span> <span class="n">configarg</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Request.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Request.py</a>.</p>
</div>
<div class="section" id="filetransfer"> <div class="section" id="filetransfer">
<span id="example-filetransfer"></span><h2>Filetransfer<a class="headerlink" href="#filetransfer" title="Permalink to this headline"></a></h2> <span id="example-filetransfer"></span><h2>Filetransfer<a class="headerlink" href="#filetransfer" title="Permalink to this headline"></a></h2>
<p>The <em>Filetransfer</em> example implements a basic file-server program that <p>The <em>Filetransfer</em> example implements a basic file-server program that
@@ -1110,7 +1724,7 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="c1"># We configure a function that will get called every time</span> <span class="c1"># We configure a function that will get called every time</span>
<span class="c1"># a new client creates a link to this destination.</span> <span class="c1"># a new client creates a link to this destination.</span>
<span class="n">server_destination</span><span class="o">.</span><span class="n">link_established_callback</span><span class="p">(</span><span class="n">client_connected</span><span class="p">)</span> <span class="n">server_destination</span><span class="o">.</span><span class="n">set_link_established_callback</span><span class="p">(</span><span class="n">client_connected</span><span class="p">)</span>
<span class="c1"># Everything&#39;s ready!</span> <span class="c1"># Everything&#39;s ready!</span>
<span class="c1"># Let&#39;s Wait for client requests or user input</span> <span class="c1"># Let&#39;s Wait for client requests or user input</span>
@@ -1147,7 +1761,7 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isdir</span><span class="p">(</span><span class="n">serve_path</span><span class="p">):</span> <span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isdir</span><span class="p">(</span><span class="n">serve_path</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client connected, sending file list...&quot;</span><span class="p">)</span> <span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client connected, sending file list...&quot;</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">link_closed_callback</span><span class="p">(</span><span class="n">client_disconnected</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_link_closed_callback</span><span class="p">(</span><span class="n">client_disconnected</span><span class="p">)</span>
<span class="c1"># We pack a list of files for sending in a packet</span> <span class="c1"># We pack a list of files for sending in a packet</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">umsgpack</span><span class="o">.</span><span class="n">packb</span><span class="p">(</span><span class="n">list_files</span><span class="p">())</span> <span class="n">data</span> <span class="o">=</span> <span class="n">umsgpack</span><span class="o">.</span><span class="n">packb</span><span class="p">(</span><span class="n">list_files</span><span class="p">())</span>
@@ -1159,8 +1773,8 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="n">list_packet</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Packet</span><span class="p">(</span><span class="n">link</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span> <span class="n">list_packet</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Packet</span><span class="p">(</span><span class="n">link</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
<span class="n">list_receipt</span> <span class="o">=</span> <span class="n">list_packet</span><span class="o">.</span><span class="n">send</span><span class="p">()</span> <span class="n">list_receipt</span> <span class="o">=</span> <span class="n">list_packet</span><span class="o">.</span><span class="n">send</span><span class="p">()</span>
<span class="n">list_receipt</span><span class="o">.</span><span class="n">set_timeout</span><span class="p">(</span><span class="n">APP_TIMEOUT</span><span class="p">)</span> <span class="n">list_receipt</span><span class="o">.</span><span class="n">set_timeout</span><span class="p">(</span><span class="n">APP_TIMEOUT</span><span class="p">)</span>
<span class="n">list_receipt</span><span class="o">.</span><span class="n">delivery_callback</span><span class="p">(</span><span class="n">list_delivered</span><span class="p">)</span> <span class="n">list_receipt</span><span class="o">.</span><span class="n">set_delivery_callback</span><span class="p">(</span><span class="n">list_delivered</span><span class="p">)</span>
<span class="n">list_receipt</span><span class="o">.</span><span class="n">timeout_callback</span><span class="p">(</span><span class="n">list_timeout</span><span class="p">)</span> <span class="n">list_receipt</span><span class="o">.</span><span class="n">set_timeout_callback</span><span class="p">(</span><span class="n">list_timeout</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span> <span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Too many files in served directory!&quot;</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_ERROR</span><span class="p">)</span> <span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Too many files in served directory!&quot;</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_ERROR</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;You should implement a function to split the filelist over multiple packets.&quot;</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_ERROR</span><span class="p">)</span> <span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;You should implement a function to split the filelist over multiple packets.&quot;</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_ERROR</span><span class="p">)</span>
@@ -1170,7 +1784,7 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="c1"># open until the client requests a file. We&#39;ll</span> <span class="c1"># open until the client requests a file. We&#39;ll</span>
<span class="c1"># configure a function that get&#39;s called when</span> <span class="c1"># configure a function that get&#39;s called when</span>
<span class="c1"># the client sends a packet with a file request.</span> <span class="c1"># the client sends a packet with a file request.</span>
<span class="n">link</span><span class="o">.</span><span class="n">packet_callback</span><span class="p">(</span><span class="n">client_request</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_packet_callback</span><span class="p">(</span><span class="n">client_request</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span> <span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client connected, but served path no longer exists!&quot;</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_ERROR</span><span class="p">)</span> <span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client connected, but served path no longer exists!&quot;</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_ERROR</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span> <span class="n">link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span>
@@ -1180,7 +1794,12 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="k">def</span> <span class="nf">client_request</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">packet</span><span class="p">):</span> <span class="k">def</span> <span class="nf">client_request</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">packet</span><span class="p">):</span>
<span class="k">global</span> <span class="n">serve_path</span> <span class="k">global</span> <span class="n">serve_path</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">filename</span> <span class="o">=</span> <span class="n">message</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span> <span class="n">filename</span> <span class="o">=</span> <span class="n">message</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">filename</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">list_files</span><span class="p">():</span> <span class="k">if</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">list_files</span><span class="p">():</span>
<span class="k">try</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span>
<span class="c1"># If we have the requested file, we&#39;ll</span> <span class="c1"># If we have the requested file, we&#39;ll</span>
@@ -1299,18 +1918,18 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="c1"># We expect any normal data packets on the link</span> <span class="c1"># We expect any normal data packets on the link</span>
<span class="c1"># to contain a list of served files, so we set</span> <span class="c1"># to contain a list of served files, so we set</span>
<span class="c1"># a callback accordingly</span> <span class="c1"># a callback accordingly</span>
<span class="n">link</span><span class="o">.</span><span class="n">packet_callback</span><span class="p">(</span><span class="n">filelist_received</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_packet_callback</span><span class="p">(</span><span class="n">filelist_received</span><span class="p">)</span>
<span class="c1"># We&#39;ll also set up functions to inform the</span> <span class="c1"># We&#39;ll also set up functions to inform the</span>
<span class="c1"># user when the link is established or closed</span> <span class="c1"># user when the link is established or closed</span>
<span class="n">link</span><span class="o">.</span><span class="n">link_established_callback</span><span class="p">(</span><span class="n">link_established</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_link_established_callback</span><span class="p">(</span><span class="n">link_established</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">link_closed_callback</span><span class="p">(</span><span class="n">link_closed</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_link_closed_callback</span><span class="p">(</span><span class="n">link_closed</span><span class="p">)</span>
<span class="c1"># And set the link to automatically begin</span> <span class="c1"># And set the link to automatically begin</span>
<span class="c1"># downloading advertised resources</span> <span class="c1"># downloading advertised resources</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_resource_strategy</span><span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">ACCEPT_ALL</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_resource_strategy</span><span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">ACCEPT_ALL</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">resource_started_callback</span><span class="p">(</span><span class="n">download_began</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_resource_started_callback</span><span class="p">(</span><span class="n">download_began</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">resource_concluded_callback</span><span class="p">(</span><span class="n">download_concluded</span><span class="p">)</span> <span class="n">link</span><span class="o">.</span><span class="n">set_resource_concluded_callback</span><span class="p">(</span><span class="n">download_concluded</span><span class="p">)</span>
<span class="n">menu</span><span class="p">()</span> <span class="n">menu</span><span class="p">()</span>
@@ -1542,7 +2161,6 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="n">saved_filename</span> <span class="o">=</span> <span class="n">current_filename</span> <span class="n">saved_filename</span> <span class="o">=</span> <span class="n">current_filename</span>
<span class="k">if</span> <span class="n">resource</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Resource</span><span class="o">.</span><span class="n">COMPLETE</span><span class="p">:</span> <span class="k">if</span> <span class="n">resource</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Resource</span><span class="o">.</span><span class="n">COMPLETE</span><span class="p">:</span>
<span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">saved_filename</span><span class="p">):</span> <span class="k">while</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">saved_filename</span><span class="p">):</span>
@@ -1661,17 +2279,16 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<li><a class="reference internal" href="#broadcast">Broadcast</a></li> <li><a class="reference internal" href="#broadcast">Broadcast</a></li>
<li><a class="reference internal" href="#echo">Echo</a></li> <li><a class="reference internal" href="#echo">Echo</a></li>
<li><a class="reference internal" href="#link">Link</a></li> <li><a class="reference internal" href="#link">Link</a></li>
<li><a class="reference internal" href="#example-identify">Identification</a></li>
<li><a class="reference internal" href="#requests-responses">Requests &amp; Responses</a></li>
<li><a class="reference internal" href="#filetransfer">Filetransfer</a></li> <li><a class="reference internal" href="#filetransfer">Filetransfer</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
<h4>Previous topic</h4> <h4>Previous topic</h4>
<p class="topless"><a href="gettingstartedfast.html"
title="previous chapter">Getting Started Fast</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="reference.html" <p class="topless"><a href="reference.html"
title="next chapter">API Reference</a></p> title="previous chapter">API Reference</a></p>
<div role="note" aria-label="source link"> <div role="note" aria-label="source link">
<h3>This Page</h3> <h3>This Page</h3>
<ul class="this-page-menu"> <ul class="this-page-menu">
@@ -1701,11 +2318,8 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
>index</a></li> >index</a></li>
<li class="right" > <li class="right" >
<a href="reference.html" title="API Reference" <a href="reference.html" title="API Reference"
>next</a> |</li>
<li class="right" >
<a href="gettingstartedfast.html" title="Getting Started Fast"
>previous</a> |</li> >previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Examples</a></li> <li class="nav-item nav-item-this"><a href="">Examples</a></li>
</ul> </ul>
</div> </div>
+62 -38
View File
@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &#8212; Reticulum Network Stack 0.2.0 beta documentation</title> <title>Index &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" /> <link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -23,7 +23,7 @@
<li class="right" style="margin-right: 10px"> <li class="right" style="margin-right: 10px">
<a href="#" title="General Index" <a href="#" title="General Index"
accesskey="I">index</a></li> accesskey="I">index</a></li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Index</a></li> <li class="nav-item nav-item-this"><a href="">Index</a></li>
</ul> </ul>
</div> </div>
@@ -80,8 +80,12 @@
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Destination.create_keys">create_keys() (RNS.Destination method)</a> <li><a href="reference.html#RNS.Destination.create_keys">create_keys() (RNS.Destination method)</a>
</li> </li>
<li><a href="reference.html#RNS.Link.CURVE">CURVE (RNS.Link attribute)</a> <li><a href="reference.html#RNS.Identity.CURVE">CURVE (RNS.Identity attribute)</a>
<ul>
<li><a href="reference.html#RNS.Link.CURVE">(RNS.Link attribute)</a>
</li> </li>
</ul></li>
</ul></td> </ul></td>
</tr></table> </tr></table>
@@ -98,9 +102,9 @@
</li> </li>
</ul></td> </ul></td>
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.PacketReceipt.delivery_callback">delivery_callback() (RNS.PacketReceipt method)</a>
</li>
<li><a href="reference.html#RNS.Transport.deregister_announce_handler">deregister_announce_handler() (RNS.Transport static method)</a> <li><a href="reference.html#RNS.Transport.deregister_announce_handler">deregister_announce_handler() (RNS.Transport static method)</a>
</li>
<li><a href="reference.html#RNS.Destination.deregister_request_handler">deregister_request_handler() (RNS.Destination method)</a>
</li> </li>
<li><a href="reference.html#RNS.Destination">Destination (class in RNS)</a> <li><a href="reference.html#RNS.Destination">Destination (class in RNS)</a>
</li> </li>
@@ -119,11 +123,17 @@
</li> </li>
</ul></li> </ul></li>
</ul></td> </ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Packet.ENCRYPTED_MDU">ENCRYPTED_MDU (RNS.Packet attribute)</a>
</li>
</ul></td>
</tr></table> </tr></table>
<h2 id="F">F</h2> <h2 id="F">F</h2>
<table style="width: 100%" class="indextable genindextable"><tr> <table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Identity.from_bytes">from_bytes() (RNS.Identity static method)</a>
</li>
<li><a href="reference.html#RNS.Identity.from_file">from_file() (RNS.Identity static method)</a> <li><a href="reference.html#RNS.Identity.from_file">from_file() (RNS.Identity static method)</a>
</li> </li>
</ul></td> </ul></td>
@@ -144,11 +154,15 @@
<li><a href="reference.html#RNS.Identity.get_private_key">(RNS.Identity method)</a> <li><a href="reference.html#RNS.Identity.get_private_key">(RNS.Identity method)</a>
</li> </li>
</ul></li> </ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Identity.get_public_key">get_public_key() (RNS.Identity method)</a> <li><a href="reference.html#RNS.Identity.get_public_key">get_public_key() (RNS.Identity method)</a>
</li> </li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Identity.get_random_hash">get_random_hash() (RNS.Identity static method)</a> <li><a href="reference.html#RNS.Identity.get_random_hash">get_random_hash() (RNS.Identity static method)</a>
</li>
<li><a href="reference.html#RNS.Link.get_remote_identity">get_remote_identity() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.PacketReceipt.get_rtt">get_rtt() (RNS.PacketReceipt method)</a>
</li> </li>
<li><a href="reference.html#RNS.PacketReceipt.get_status">get_status() (RNS.PacketReceipt method)</a> <li><a href="reference.html#RNS.PacketReceipt.get_status">get_status() (RNS.PacketReceipt method)</a>
</li> </li>
@@ -172,10 +186,12 @@
<h2 id="I">I</h2> <h2 id="I">I</h2>
<table style="width: 100%" class="indextable genindextable"><tr> <table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Identity">Identity (class in RNS)</a> <li><a href="reference.html#RNS.Link.identify">identify() (RNS.Link method)</a>
</li> </li>
</ul></td> </ul></td>
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Identity">Identity (class in RNS)</a>
</li>
<li><a href="reference.html#RNS.Link.inactive_for">inactive_for() (RNS.Link method)</a> <li><a href="reference.html#RNS.Link.inactive_for">inactive_for() (RNS.Link method)</a>
</li> </li>
</ul></td> </ul></td>
@@ -198,16 +214,14 @@
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Link">Link (class in RNS)</a> <li><a href="reference.html#RNS.Link">Link (class in RNS)</a>
</li> </li>
<li><a href="reference.html#RNS.Destination.link_established_callback">link_established_callback() (RNS.Destination method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Destination.load_private_key">load_private_key() (RNS.Destination method)</a> <li><a href="reference.html#RNS.Destination.load_private_key">load_private_key() (RNS.Destination method)</a>
<ul> <ul>
<li><a href="reference.html#RNS.Identity.load_private_key">(RNS.Identity method)</a> <li><a href="reference.html#RNS.Identity.load_private_key">(RNS.Identity method)</a>
</li> </li>
</ul></li> </ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Identity.load_public_key">load_public_key() (RNS.Identity method)</a> <li><a href="reference.html#RNS.Identity.load_public_key">load_public_key() (RNS.Identity method)</a>
</li> </li>
</ul></td> </ul></td>
@@ -230,19 +244,13 @@
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Packet">Packet (class in RNS)</a> <li><a href="reference.html#RNS.Packet">Packet (class in RNS)</a>
</li> </li>
<li><a href="reference.html#RNS.Destination.packet_callback">packet_callback() (RNS.Destination method)</a>
<ul>
<li><a href="reference.html#RNS.Link.packet_callback">(RNS.Link method)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.PacketReceipt">PacketReceipt (class in RNS)</a> <li><a href="reference.html#RNS.PacketReceipt">PacketReceipt (class in RNS)</a>
</li> </li>
<li><a href="reference.html#RNS.Resource.progress">progress() (RNS.Resource method)</a> </ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Packet.PLAIN_MDU">PLAIN_MDU (RNS.Packet attribute)</a>
</li> </li>
<li><a href="reference.html#RNS.Destination.proof_requested_callback">proof_requested_callback() (RNS.Destination method)</a> <li><a href="reference.html#RNS.Resource.progress">progress() (RNS.Resource method)</a>
</li> </li>
</ul></td> </ul></td>
</tr></table> </tr></table>
@@ -255,24 +263,20 @@
<li><a href="reference.html#RNS.Identity.recall_app_data">recall_app_data() (RNS.Identity static method)</a> <li><a href="reference.html#RNS.Identity.recall_app_data">recall_app_data() (RNS.Identity static method)</a>
</li> </li>
<li><a href="reference.html#RNS.Transport.register_announce_handler">register_announce_handler() (RNS.Transport static method)</a> <li><a href="reference.html#RNS.Transport.register_announce_handler">register_announce_handler() (RNS.Transport static method)</a>
</li>
<li><a href="reference.html#RNS.Destination.register_request_handler">register_request_handler() (RNS.Destination method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Link.request">request() (RNS.Link method)</a>
</li> </li>
<li><a href="reference.html#RNS.Transport.request_path">request_path() (RNS.Transport static method)</a> <li><a href="reference.html#RNS.Transport.request_path">request_path() (RNS.Transport static method)</a>
</li> </li>
<li><a href="reference.html#RNS.Packet.resend">resend() (RNS.Packet method)</a> <li><a href="reference.html#RNS.Packet.resend">resend() (RNS.Packet method)</a>
</li> </li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Resource">Resource (class in RNS)</a> <li><a href="reference.html#RNS.Resource">Resource (class in RNS)</a>
</li>
<li><a href="reference.html#RNS.Link.resource_callback">resource_callback() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.Link.resource_concluded_callback">resource_concluded_callback() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.Link.resource_started_callback">resource_started_callback() (RNS.Link method)</a>
</li> </li>
<li><a href="reference.html#RNS.Reticulum">Reticulum (class in RNS)</a> <li><a href="reference.html#RNS.Reticulum">Reticulum (class in RNS)</a>
</li>
<li><a href="reference.html#RNS.PacketReceipt.rtt">rtt() (RNS.PacketReceipt method)</a>
</li> </li>
</ul></td> </ul></td>
</tr></table> </tr></table>
@@ -283,14 +287,36 @@
<li><a href="reference.html#RNS.Packet.send">send() (RNS.Packet method)</a> <li><a href="reference.html#RNS.Packet.send">send() (RNS.Packet method)</a>
</li> </li>
<li><a href="reference.html#RNS.Destination.set_default_app_data">set_default_app_data() (RNS.Destination method)</a> <li><a href="reference.html#RNS.Destination.set_default_app_data">set_default_app_data() (RNS.Destination method)</a>
</li>
<li><a href="reference.html#RNS.PacketReceipt.set_delivery_callback">set_delivery_callback() (RNS.PacketReceipt method)</a>
</li>
<li><a href="reference.html#RNS.Destination.set_link_established_callback">set_link_established_callback() (RNS.Destination method)</a>
</li>
<li><a href="reference.html#RNS.Destination.set_packet_callback">set_packet_callback() (RNS.Destination method)</a>
<ul>
<li><a href="reference.html#RNS.Link.set_packet_callback">(RNS.Link method)</a>
</li>
</ul></li>
<li><a href="reference.html#RNS.Destination.set_proof_requested_callback">set_proof_requested_callback() (RNS.Destination method)</a>
</li> </li>
<li><a href="reference.html#RNS.Destination.set_proof_strategy">set_proof_strategy() (RNS.Destination method)</a> <li><a href="reference.html#RNS.Destination.set_proof_strategy">set_proof_strategy() (RNS.Destination method)</a>
</li> </li>
<li><a href="reference.html#RNS.Link.set_resource_strategy">set_resource_strategy() (RNS.Link method)</a> <li><a href="reference.html#RNS.Link.set_remote_identified_callback">set_remote_identified_callback() (RNS.Link method)</a>
</li> </li>
</ul></td> </ul></td>
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Link.set_resource_callback">set_resource_callback() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.Link.set_resource_concluded_callback">set_resource_concluded_callback() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.Link.set_resource_started_callback">set_resource_started_callback() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.Link.set_resource_strategy">set_resource_strategy() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.PacketReceipt.set_timeout">set_timeout() (RNS.PacketReceipt method)</a> <li><a href="reference.html#RNS.PacketReceipt.set_timeout">set_timeout() (RNS.PacketReceipt method)</a>
</li>
<li><a href="reference.html#RNS.PacketReceipt.set_timeout_callback">set_timeout_callback() (RNS.PacketReceipt method)</a>
</li> </li>
<li><a href="reference.html#RNS.Reticulum.should_allow_unencrypted">should_allow_unencrypted() (RNS.Reticulum static method)</a> <li><a href="reference.html#RNS.Reticulum.should_allow_unencrypted">should_allow_unencrypted() (RNS.Reticulum static method)</a>
</li> </li>
@@ -309,15 +335,13 @@
<table style="width: 100%" class="indextable genindextable"><tr> <table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Link.teardown">teardown() (RNS.Link method)</a> <li><a href="reference.html#RNS.Link.teardown">teardown() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.PacketReceipt.timeout_callback">timeout_callback() (RNS.PacketReceipt method)</a>
</li> </li>
<li><a href="reference.html#RNS.Identity.to_file">to_file() (RNS.Identity method)</a> <li><a href="reference.html#RNS.Identity.to_file">to_file() (RNS.Identity method)</a>
</li>
<li><a href="reference.html#RNS.Transport">Transport (class in RNS)</a>
</li> </li>
</ul></td> </ul></td>
<td style="width: 33%; vertical-align: top;"><ul> <td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Transport">Transport (class in RNS)</a>
</li>
<li><a href="reference.html#RNS.Reticulum.transport_enabled">transport_enabled() (RNS.Reticulum static method)</a> <li><a href="reference.html#RNS.Reticulum.transport_enabled">transport_enabled() (RNS.Reticulum static method)</a>
</li> </li>
<li><a href="reference.html#RNS.Identity.truncated_hash">truncated_hash() (RNS.Identity static method)</a> <li><a href="reference.html#RNS.Identity.truncated_hash">truncated_hash() (RNS.Identity static method)</a>
@@ -363,7 +387,7 @@
<li class="right" style="margin-right: 10px"> <li class="right" style="margin-right: 10px">
<a href="#" title="General Index" <a href="#" title="General Index"
>index</a></li> >index</a></li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Index</a></li> <li class="nav-item nav-item-this"><a href="">Index</a></li>
</ul> </ul>
</div> </div>
+9 -10
View File
@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Getting Started Fast &#8212; Reticulum Network Stack 0.2.0 beta documentation</title> <title>Getting Started Fast &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" /> <link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -16,7 +16,7 @@
<link rel="index" title="Index" href="genindex.html" /> <link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" /> <link rel="search" title="Search" href="search.html" />
<link rel="next" title="Examples" href="examples.html" /> <link rel="next" title="Understanding Reticulum" href="understanding.html" />
<link rel="prev" title="What is Reticulum?" href="whatis.html" /> <link rel="prev" title="What is Reticulum?" href="whatis.html" />
</head><body> </head><body>
<div class="related" role="navigation" aria-label="related navigation"> <div class="related" role="navigation" aria-label="related navigation">
@@ -26,12 +26,12 @@
<a href="genindex.html" title="General Index" <a href="genindex.html" title="General Index"
accesskey="I">index</a></li> accesskey="I">index</a></li>
<li class="right" > <li class="right" >
<a href="examples.html" title="Examples" <a href="understanding.html" title="Understanding Reticulum"
accesskey="N">next</a> |</li> accesskey="N">next</a> |</li>
<li class="right" > <li class="right" >
<a href="whatis.html" title="What is Reticulum?" <a href="whatis.html" title="What is Reticulum?"
accesskey="P">previous</a> |</li> accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Getting Started Fast</a></li> <li class="nav-item nav-item-this"><a href="">Getting Started Fast</a></li>
</ul> </ul>
</div> </div>
@@ -43,7 +43,6 @@
<div class="section" id="getting-started-fast"> <div class="section" id="getting-started-fast">
<h1>Getting Started Fast<a class="headerlink" href="#getting-started-fast" title="Permalink to this headline"></a></h1> <h1>Getting Started Fast<a class="headerlink" href="#getting-started-fast" title="Permalink to this headline"></a></h1>
<p>What do we want to do? Something! When do we want to do it? Right now! Lets go.</p>
<p>The best way to get started with the Reticulum Network Stack depends on what <p>The best way to get started with the Reticulum Network Stack depends on what
you want to do. This guide will outline sensible starting paths for different you want to do. This guide will outline sensible starting paths for different
scenarios.</p> scenarios.</p>
@@ -60,7 +59,7 @@ in the development for the messaging and information-sharing protocol
<div class="section" id="develop-a-program-with-reticulum"> <div class="section" id="develop-a-program-with-reticulum">
<h2>Develop a Program with Reticulum<a class="headerlink" href="#develop-a-program-with-reticulum" title="Permalink to this headline"></a></h2> <h2>Develop a Program with Reticulum<a class="headerlink" href="#develop-a-program-with-reticulum" title="Permalink to this headline"></a></h2>
<p>If you want to develop programs that use Reticulum, the easiest way to get <p>If you want to develop programs that use Reticulum, the easiest way to get
started is to install Reticulum via pip:</p> started is to install the latest release of Reticulum via pip:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip3</span> <span class="n">install</span> <span class="n">rns</span> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip3</span> <span class="n">install</span> <span class="n">rns</span>
</pre></div> </pre></div>
</div> </div>
@@ -132,8 +131,8 @@ dont use pip, but try this recipe:</p>
<p class="topless"><a href="whatis.html" <p class="topless"><a href="whatis.html"
title="previous chapter">What is Reticulum?</a></p> title="previous chapter">What is Reticulum?</a></p>
<h4>Next topic</h4> <h4>Next topic</h4>
<p class="topless"><a href="examples.html" <p class="topless"><a href="understanding.html"
title="next chapter">Examples</a></p> title="next chapter">Understanding Reticulum</a></p>
<div role="note" aria-label="source link"> <div role="note" aria-label="source link">
<h3>This Page</h3> <h3>This Page</h3>
<ul class="this-page-menu"> <ul class="this-page-menu">
@@ -162,12 +161,12 @@ dont use pip, but try this recipe:</p>
<a href="genindex.html" title="General Index" <a href="genindex.html" title="General Index"
>index</a></li> >index</a></li>
<li class="right" > <li class="right" >
<a href="examples.html" title="Examples" <a href="understanding.html" title="Understanding Reticulum"
>next</a> |</li> >next</a> |</li>
<li class="right" > <li class="right" >
<a href="whatis.html" title="What is Reticulum?" <a href="whatis.html" title="What is Reticulum?"
>previous</a> |</li> >previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Getting Started Fast</a></li> <li class="nav-item nav-item-this"><a href="">Getting Started Fast</a></li>
</ul> </ul>
</div> </div>
+35 -32
View File
@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Reticulum Network Stack Manual &#8212; Reticulum Network Stack 0.2.0 beta documentation</title> <title>Reticulum Network Stack Manual &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" /> <link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -27,7 +27,7 @@
<li class="right" > <li class="right" >
<a href="whatis.html" title="What is Reticulum?" <a href="whatis.html" title="What is Reticulum?"
accesskey="N">next</a> |</li> accesskey="N">next</a> |</li>
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Reticulum Network Stack Manual</a></li> <li class="nav-item nav-item-this"><a href="">Reticulum Network Stack Manual</a></li>
</ul> </ul>
</div> </div>
@@ -58,13 +58,29 @@ the development of Reticulum itself.</p>
<li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#participate-in-reticulum-development">Participate in Reticulum Development</a></li> <li class="toctree-l2"><a class="reference internal" href="gettingstartedfast.html#participate-in-reticulum-development">Participate in Reticulum Development</a></li>
</ul> </ul>
</li> </li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Examples</a><ul> <li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a><ul>
<li class="toctree-l2"><a class="reference internal" href="examples.html#minimal">Minimal</a></li> <li class="toctree-l2"><a class="reference internal" href="understanding.html#motivation">Motivation</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#announce">Announce</a></li> <li class="toctree-l2"><a class="reference internal" href="understanding.html#goals">Goals</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#broadcast">Broadcast</a></li> <li class="toctree-l2"><a class="reference internal" href="understanding.html#introduction-basic-functionality">Introduction &amp; Basic Functionality</a><ul>
<li class="toctree-l2"><a class="reference internal" href="examples.html#echo">Echo</a></li> <li class="toctree-l3"><a class="reference internal" href="understanding.html#destinations">Destinations</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#link">Link</a></li> <li class="toctree-l3"><a class="reference internal" href="understanding.html#public-key-announcements">Public Key Announcements</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#filetransfer">Filetransfer</a></li> <li class="toctree-l3"><a class="reference internal" href="understanding.html#understanding-identities">Identities</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#getting-further">Getting Further</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="understanding.html#reticulum-transport">Reticulum Transport</a><ul>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#the-announce-mechanism-in-detail">The Announce Mechanism in Detail</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#reaching-the-destination">Reaching the Destination</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#resources">Resources</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="understanding.html#reference-system-setup">Reference System Setup</a></li>
<li class="toctree-l2"><a class="reference internal" href="understanding.html#protocol-specifics">Protocol Specifics</a><ul>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#node-types">Node Types</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#packet-prioritisation">Packet Prioritisation</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#binary-packet-format">Binary Packet Format</a></li>
</ul>
</li>
</ul> </ul>
</li> </li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">API Reference</a><ul> <li class="toctree-l1"><a class="reference internal" href="reference.html">API Reference</a><ul>
@@ -81,28 +97,15 @@ the development of Reticulum itself.</p>
</li> </li>
</ul> </ul>
</li> </li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a><ul> <li class="toctree-l1"><a class="reference internal" href="examples.html">Examples</a><ul>
<li class="toctree-l2"><a class="reference internal" href="understanding.html#motivation">Motivation</a></li> <li class="toctree-l2"><a class="reference internal" href="examples.html#minimal">Minimal</a></li>
<li class="toctree-l2"><a class="reference internal" href="understanding.html#goals">Goals</a></li> <li class="toctree-l2"><a class="reference internal" href="examples.html#announce">Announce</a></li>
<li class="toctree-l2"><a class="reference internal" href="understanding.html#introduction-basic-functionality">Introduction &amp; Basic Functionality</a><ul> <li class="toctree-l2"><a class="reference internal" href="examples.html#broadcast">Broadcast</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#destinations">Destinations</a></li> <li class="toctree-l2"><a class="reference internal" href="examples.html#echo">Echo</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#public-key-announcements">Public Key Announcements</a></li> <li class="toctree-l2"><a class="reference internal" href="examples.html#link">Link</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#understanding-identities">Identities</a></li> <li class="toctree-l2"><a class="reference internal" href="examples.html#example-identify">Identification</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#getting-further">Getting Further</a></li> <li class="toctree-l2"><a class="reference internal" href="examples.html#requests-responses">Requests &amp; Responses</a></li>
</ul> <li class="toctree-l2"><a class="reference internal" href="examples.html#filetransfer">Filetransfer</a></li>
</li>
<li class="toctree-l2"><a class="reference internal" href="understanding.html#reticulum-transport">Reticulum Transport</a><ul>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#reaching-the-destination">Reaching the Destination</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#resources">Resources</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="understanding.html#reference-system-setup">Reference System Setup</a></li>
<li class="toctree-l2"><a class="reference internal" href="understanding.html#protocol-specifics">Protocol Specifics</a><ul>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#node-types">Node Types</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#packet-prioritisation">Packet Prioritisation</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#binary-packet-format">Binary Packet Format</a></li>
</ul>
</li>
</ul> </ul>
</li> </li>
</ul> </ul>
@@ -164,7 +167,7 @@ the development of Reticulum itself.</p>
<li class="right" > <li class="right" >
<a href="whatis.html" title="What is Reticulum?" <a href="whatis.html" title="What is Reticulum?"
>next</a> |</li> >next</a> |</li>
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Reticulum Network Stack Manual</a></li> <li class="nav-item nav-item-this"><a href="">Reticulum Network Stack Manual</a></li>
</ul> </ul>
</div> </div>
Binary file not shown.
+177 -53
View File
@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>API Reference &#8212; Reticulum Network Stack 0.2.0 beta documentation</title> <title>API Reference &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" /> <link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -16,8 +16,8 @@
<link rel="index" title="Index" href="genindex.html" /> <link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" /> <link rel="search" title="Search" href="search.html" />
<link rel="next" title="Understanding Reticulum" href="understanding.html" /> <link rel="next" title="Examples" href="examples.html" />
<link rel="prev" title="Examples" href="examples.html" /> <link rel="prev" title="Understanding Reticulum" href="understanding.html" />
</head><body> </head><body>
<div class="related" role="navigation" aria-label="related navigation"> <div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3> <h3>Navigation</h3>
@@ -26,12 +26,12 @@
<a href="genindex.html" title="General Index" <a href="genindex.html" title="General Index"
accesskey="I">index</a></li> accesskey="I">index</a></li>
<li class="right" > <li class="right" >
<a href="understanding.html" title="Understanding Reticulum" <a href="examples.html" title="Examples"
accesskey="N">next</a> |</li> accesskey="N">next</a> |</li>
<li class="right" > <li class="right" >
<a href="examples.html" title="Examples" <a href="understanding.html" title="Understanding Reticulum"
accesskey="P">previous</a> |</li> accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">API Reference</a></li> <li class="nav-item nav-item-this"><a href="">API Reference</a></li>
</ul> </ul>
</div> </div>
@@ -117,26 +117,32 @@ and pass announces over the network.</p>
<span id="identity"></span><h3>Identity<a class="headerlink" href="#api-identity" title="Permalink to this headline"></a></h3> <span id="identity"></span><h3>Identity<a class="headerlink" href="#api-identity" title="Permalink to this headline"></a></h3>
<dl class="py class"> <dl class="py class">
<dt class="sig sig-object py" id="RNS.Identity"> <dt class="sig sig-object py" id="RNS.Identity">
<em class="property"><span class="pre">class</span> </em><span class="sig-prename descclassname"><span class="pre">RNS.</span></span><span class="sig-name descname"><span class="pre">Identity</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">public_only</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity" title="Permalink to this definition"></a></dt> <em class="property"><span class="pre">class</span> </em><span class="sig-prename descclassname"><span class="pre">RNS.</span></span><span class="sig-name descname"><span class="pre">Identity</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">create_keys</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity" title="Permalink to this definition"></a></dt>
<dd><p>This class is used to manage identities in Reticulum. It provides methods <dd><p>This class is used to manage identities in Reticulum. It provides methods
for encryption, decryption, signatures and verification, and is the basis for encryption, decryption, signatures and verification, and is the basis
for all encrypted communication over Reticulum networks.</p> for all encrypted communication over Reticulum networks.</p>
<dl class="field-list simple"> <dl class="field-list simple">
<dt class="field-odd">Parameters</dt> <dt class="field-odd">Parameters</dt>
<dd class="field-odd"><p><strong>public_only</strong> Specifies whether this destination only holds a public key.</p> <dd class="field-odd"><p><strong>create_keys</strong> Specifies whether new encryption and signing keys should be generated.</p>
</dd> </dd>
</dl> </dl>
<dl class="py attribute">
<dt class="sig sig-object py" id="RNS.Identity.CURVE">
<span class="sig-name descname"><span class="pre">CURVE</span></span><em class="property"> <span class="pre">=</span> <span class="pre">'Curve25519'</span></em><a class="headerlink" href="#RNS.Identity.CURVE" title="Permalink to this definition"></a></dt>
<dd><p>The curve used for Elliptic Curve DH key exchanges</p>
</dd></dl>
<dl class="py attribute"> <dl class="py attribute">
<dt class="sig sig-object py" id="RNS.Identity.KEYSIZE"> <dt class="sig sig-object py" id="RNS.Identity.KEYSIZE">
<span class="sig-name descname"><span class="pre">KEYSIZE</span></span><em class="property"> <span class="pre">=</span> <span class="pre">1024</span></em><a class="headerlink" href="#RNS.Identity.KEYSIZE" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">KEYSIZE</span></span><em class="property"> <span class="pre">=</span> <span class="pre">512</span></em><a class="headerlink" href="#RNS.Identity.KEYSIZE" title="Permalink to this definition"></a></dt>
<dd><p>RSA key size in bits.</p> <dd><p>X25519 key size in bits. A complete key is the concatenation of a 256 bit encryption key, and a 256 bit signing key.</p>
</dd></dl> </dd></dl>
<dl class="py attribute"> <dl class="py attribute">
<dt class="sig sig-object py" id="RNS.Identity.TRUNCATED_HASHLENGTH"> <dt class="sig sig-object py" id="RNS.Identity.TRUNCATED_HASHLENGTH">
<span class="sig-name descname"><span class="pre">TRUNCATED_HASHLENGTH</span></span><em class="property"> <span class="pre">=</span> <span class="pre">80</span></em><a class="headerlink" href="#RNS.Identity.TRUNCATED_HASHLENGTH" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">TRUNCATED_HASHLENGTH</span></span><em class="property"> <span class="pre">=</span> <span class="pre">80</span></em><a class="headerlink" href="#RNS.Identity.TRUNCATED_HASHLENGTH" title="Permalink to this definition"></a></dt>
<dd><p>Constant specifying the truncated hash length (in bits) used by Reticulum <dd><p>Constant specifying the truncated hash length (in bits) used by Reticulum
for addressable hashes. Non-configurable.</p> for addressable hashes and other purposes. Non-configurable.</p>
</dd></dl> </dd></dl>
<dl class="py method"> <dl class="py method">
@@ -224,6 +230,21 @@ Can be used to load previously created and saved identities into Reticulum.</p>
</dl> </dl>
</dd></dl> </dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Identity.from_bytes">
<em class="property"><span class="pre">static</span> </em><span class="sig-name descname"><span class="pre">from_bytes</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">prv_bytes</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity.from_bytes" title="Permalink to this definition"></a></dt>
<dd><p>Create a new <a class="reference internal" href="#api-identity"><span class="std std-ref">RNS.Identity</span></a> instance from <em>bytes</em> of private key.
Can be used to load previously created and saved identities into Reticulum.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><p><strong>prv_bytes</strong> The <em>bytes</em> of private a saved private key. <strong>HAZARD!</strong> Never not use this to generate a new key by feeding random data in prv_bytes.</p>
</dd>
<dt class="field-even">Returns</dt>
<dd class="field-even"><p>A <a class="reference internal" href="#api-identity"><span class="std std-ref">RNS.Identity</span></a> instance, or <em>None</em> if the <em>bytes</em> data was invalid.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Identity.get_private_key"> <dt class="sig sig-object py" id="RNS.Identity.get_private_key">
<span class="sig-name descname"><span class="pre">get_private_key</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity.get_private_key" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">get_private_key</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity.get_private_key" title="Permalink to this definition"></a></dt>
@@ -260,11 +281,11 @@ Can be used to load previously created and saved identities into Reticulum.</p>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Identity.load_public_key"> <dt class="sig sig-object py" id="RNS.Identity.load_public_key">
<span class="sig-name descname"><span class="pre">load_public_key</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">key</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity.load_public_key" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">load_public_key</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">pub_bytes</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity.load_public_key" title="Permalink to this definition"></a></dt>
<dd><p>Load a public key into the instance.</p> <dd><p>Load a public key into the instance.</p>
<dl class="field-list simple"> <dl class="field-list simple">
<dt class="field-odd">Parameters</dt> <dt class="field-odd">Parameters</dt>
<dd class="field-odd"><p><strong>prv_bytes</strong> The public key as <em>bytes</em>.</p> <dd class="field-odd"><p><strong>pub_bytes</strong> The public key as <em>bytes</em>.</p>
</dd> </dd>
<dt class="field-even">Returns</dt> <dt class="field-even">Returns</dt>
<dd class="field-even"><p>True if the key was loaded, otherwise False.</p> <dd class="field-even"><p>True if the key was loaded, otherwise False.</p>
@@ -297,17 +318,17 @@ communication for the identity. Be very careful with this method.</p>
<dd class="field-odd"><p><strong>plaintext</strong> The plaintext to be encrypted as <em>bytes</em>.</p> <dd class="field-odd"><p><strong>plaintext</strong> The plaintext to be encrypted as <em>bytes</em>.</p>
</dd> </dd>
<dt class="field-even">Returns</dt> <dt class="field-even">Returns</dt>
<dd class="field-even"><p>Ciphertext as <em>bytes</em>.</p> <dd class="field-even"><p>Ciphertext token as <em>bytes</em>.</p>
</dd> </dd>
<dt class="field-odd">Raises</dt> <dt class="field-odd">Raises</dt>
<dd class="field-odd"><p><em>KeyError</em> if the instance does not hold a public key</p> <dd class="field-odd"><p><em>KeyError</em> if the instance does not hold a public key.</p>
</dd> </dd>
</dl> </dl>
</dd></dl> </dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Identity.decrypt"> <dt class="sig sig-object py" id="RNS.Identity.decrypt">
<span class="sig-name descname"><span class="pre">decrypt</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">ciphertext</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity.decrypt" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">decrypt</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">ciphertext_token</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity.decrypt" title="Permalink to this definition"></a></dt>
<dd><p>Decrypts information for the identity.</p> <dd><p>Decrypts information for the identity.</p>
<dl class="field-list simple"> <dl class="field-list simple">
<dt class="field-odd">Parameters</dt> <dt class="field-odd">Parameters</dt>
@@ -317,7 +338,7 @@ communication for the identity. Be very careful with this method.</p>
<dd class="field-even"><p>Plaintext as <em>bytes</em>, or <em>None</em> if decryption fails.</p> <dd class="field-even"><p>Plaintext as <em>bytes</em>, or <em>None</em> if decryption fails.</p>
</dd> </dd>
<dt class="field-odd">Raises</dt> <dt class="field-odd">Raises</dt>
<dd class="field-odd"><p><em>KeyError</em> if the instance does not hold a private key</p> <dd class="field-odd"><p><em>KeyError</em> if the instance does not hold a private key.</p>
</dd> </dd>
</dl> </dl>
</dd></dl> </dd></dl>
@@ -334,7 +355,7 @@ communication for the identity. Be very careful with this method.</p>
<dd class="field-even"><p>Signature as <em>bytes</em>.</p> <dd class="field-even"><p>Signature as <em>bytes</em>.</p>
</dd> </dd>
<dt class="field-odd">Raises</dt> <dt class="field-odd">Raises</dt>
<dd class="field-odd"><p><em>KeyError</em> if the instance does not hold a private key</p> <dd class="field-odd"><p><em>KeyError</em> if the instance does not hold a private key.</p>
</dd> </dd>
</dl> </dl>
</dd></dl> </dd></dl>
@@ -354,7 +375,7 @@ communication for the identity. Be very careful with this method.</p>
<dd class="field-even"><p>True if the signature is valid, otherwise False.</p> <dd class="field-even"><p>True if the signature is valid, otherwise False.</p>
</dd> </dd>
<dt class="field-odd">Raises</dt> <dt class="field-odd">Raises</dt>
<dd class="field-odd"><p><em>KeyError</em> if the instance does not hold a public key</p> <dd class="field-odd"><p><em>KeyError</em> if the instance does not hold a public key.</p>
</dd> </dd>
</dl> </dl>
</dd></dl> </dd></dl>
@@ -427,8 +448,8 @@ encrypted communication with it.</p>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.announce"> <dt class="sig sig-object py" id="RNS.Destination.announce">
<span class="sig-name descname"><span class="pre">announce</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">app_data</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">path_response</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.announce" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">announce</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">app_data</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">path_response</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.announce" title="Permalink to this definition"></a></dt>
<dd><p>Creates an announce packet for this destination and broadcasts it on <dd><p>Creates an announce packet for this destination and broadcasts it on all
all interfaces. Application specific data can be added to the announce.</p> relevant interfaces. Application specific data can be added to the announce.</p>
<dl class="field-list simple"> <dl class="field-list simple">
<dt class="field-odd">Parameters</dt> <dt class="field-odd">Parameters</dt>
<dd class="field-odd"><ul class="simple"> <dd class="field-odd"><ul class="simple">
@@ -440,8 +461,8 @@ all interfaces. Application specific data can be added to the announce.</p>
</dd></dl> </dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.link_established_callback"> <dt class="sig sig-object py" id="RNS.Destination.set_link_established_callback">
<span class="sig-name descname"><span class="pre">link_established_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.link_established_callback" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">set_link_established_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.set_link_established_callback" title="Permalink to this definition"></a></dt>
<dd><p>Registers a function to be called when a link has been established to <dd><p>Registers a function to be called when a link has been established to
this destination.</p> this destination.</p>
<dl class="field-list simple"> <dl class="field-list simple">
@@ -452,8 +473,8 @@ this destination.</p>
</dd></dl> </dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.packet_callback"> <dt class="sig sig-object py" id="RNS.Destination.set_packet_callback">
<span class="sig-name descname"><span class="pre">packet_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.packet_callback" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">set_packet_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.set_packet_callback" title="Permalink to this definition"></a></dt>
<dd><p>Registers a function to be called when a packet has been received by <dd><p>Registers a function to be called when a packet has been received by
this destination.</p> this destination.</p>
<dl class="field-list simple"> <dl class="field-list simple">
@@ -464,8 +485,8 @@ this destination.</p>
</dd></dl> </dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.proof_requested_callback"> <dt class="sig sig-object py" id="RNS.Destination.set_proof_requested_callback">
<span class="sig-name descname"><span class="pre">proof_requested_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.proof_requested_callback" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">set_proof_requested_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.set_proof_requested_callback" title="Permalink to this definition"></a></dt>
<dd><p>Registers a function to be called when a proof has been requested for <dd><p>Registers a function to be called when a proof has been requested for
a packet sent to this destination. Allows control over when and if a packet sent to this destination. Allows control over when and if
proofs should be returned for received packets.</p> proofs should be returned for received packets.</p>
@@ -487,6 +508,39 @@ proofs should be returned for received packets.</p>
</dl> </dl>
</dd></dl> </dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.register_request_handler">
<span class="sig-name descname"><span class="pre">register_request_handler</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">path</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">response_generator</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">allow</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">allowed_list</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.register_request_handler" title="Permalink to this definition"></a></dt>
<dd><p>Registers a request handler.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>path</strong> The path for the request handler to be registered.</p></li>
<li><p><strong>response_generator</strong> A function or method with the signature <em>response_generator(path, data, request_id, remote_identity, requested_at)</em> to be called. Whatever this funcion returns will be sent as a response to the requester. If the function returns <code class="docutils literal notranslate"><span class="pre">None</span></code>, no response will be sent.</p></li>
<li><p><strong>allow</strong> One of <code class="docutils literal notranslate"><span class="pre">RNS.Destination.ALLOW_NONE</span></code>, <code class="docutils literal notranslate"><span class="pre">RNS.Destination.ALLOW_ALL</span></code> or <code class="docutils literal notranslate"><span class="pre">RNS.Destination.ALLOW_LIST</span></code>. If <code class="docutils literal notranslate"><span class="pre">RNS.Destination.ALLOW_LIST</span></code> is set, the request handler will only respond to requests for identified peers in the supplied list.</p></li>
<li><p><strong>allowed_list</strong> A list of <em>bytes-like</em> <a class="reference internal" href="#api-identity"><span class="std std-ref">RNS.Identity</span></a> hashes.</p></li>
</ul>
</dd>
<dt class="field-even">Raises</dt>
<dd class="field-even"><p><code class="docutils literal notranslate"><span class="pre">ValueError</span></code> if any of the supplied arguments are invalid.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.deregister_request_handler">
<span class="sig-name descname"><span class="pre">deregister_request_handler</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">path</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.deregister_request_handler" title="Permalink to this definition"></a></dt>
<dd><p>Deregisters a request handler.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><p><strong>path</strong> The path for the request handler to be deregistered.</p>
</dd>
<dt class="field-even">Returns</dt>
<dd class="field-even"><p>True if the handler was deregistered, otherwise False.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.create_keys"> <dt class="sig sig-object py" id="RNS.Destination.create_keys">
<span class="sig-name descname"><span class="pre">create_keys</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.create_keys" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">create_keys</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.create_keys" title="Permalink to this definition"></a></dt>
@@ -592,8 +646,14 @@ unless other app_data is specified in the <em>announce</em> method.</p>
<dl class="py class"> <dl class="py class">
<dt class="sig sig-object py" id="RNS.Packet"> <dt class="sig sig-object py" id="RNS.Packet">
<em class="property"><span class="pre">class</span> </em><span class="sig-prename descclassname"><span class="pre">RNS.</span></span><span class="sig-name descname"><span class="pre">Packet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">destination</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">packet_type</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">context</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">transport_type</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">header_type</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">transport_id</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">attached_interface</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">create_receipt</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Packet" title="Permalink to this definition"></a></dt> <em class="property"><span class="pre">class</span> </em><span class="sig-prename descclassname"><span class="pre">RNS.</span></span><span class="sig-name descname"><span class="pre">Packet</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">destination</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">packet_type</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">context</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">transport_type</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">header_type</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">transport_id</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">attached_interface</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">create_receipt</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Packet" title="Permalink to this definition"></a></dt>
<dd><p>The Packet class is used to create packet instances that can be <dd><p>The Packet class is used to create packet instances that can be sent
sent over a Reticulum network.</p> over a Reticulum network. Packets to will automatically be encrypted if
they are adressed to a <code class="docutils literal notranslate"><span class="pre">RNS.Destination.SINGLE</span></code> destination,
<code class="docutils literal notranslate"><span class="pre">RNS.Destination.GROUP</span></code> destination or a <a class="reference internal" href="#api-link"><span class="std std-ref">RNS.Link</span></a>.</p>
<p>For <code class="docutils literal notranslate"><span class="pre">RNS.Destination.GROUP</span></code> destinations, Reticulum will use the
pre-shared key configured for the destination.</p>
<p>For <code class="docutils literal notranslate"><span class="pre">RNS.Destination.SINGLE</span></code> destinations and <a class="reference internal" href="#api-link"><span class="std std-ref">RNS.Link</span></a>
destinations, reticulum will use ephemeral keys, and offers <strong>Forward Secrecy</strong>.</p>
<dl class="field-list simple"> <dl class="field-list simple">
<dt class="field-odd">Parameters</dt> <dt class="field-odd">Parameters</dt>
<dd class="field-odd"><ul class="simple"> <dd class="field-odd"><ul class="simple">
@@ -608,6 +668,18 @@ sent over a Reticulum network.</p>
</ul> </ul>
</dd> </dd>
</dl> </dl>
<dl class="py attribute">
<dt class="sig sig-object py" id="RNS.Packet.ENCRYPTED_MDU">
<span class="sig-name descname"><span class="pre">ENCRYPTED_MDU</span></span><em class="property"> <span class="pre">=</span> <span class="pre">383</span></em><a class="headerlink" href="#RNS.Packet.ENCRYPTED_MDU" title="Permalink to this definition"></a></dt>
<dd><p>The maximum size of the payload data in a single encrypted packet</p>
</dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py" id="RNS.Packet.PLAIN_MDU">
<span class="sig-name descname"><span class="pre">PLAIN_MDU</span></span><em class="property"> <span class="pre">=</span> <span class="pre">477</span></em><a class="headerlink" href="#RNS.Packet.PLAIN_MDU" title="Permalink to this definition"></a></dt>
<dd><p>The maximum size of the payload data in a single unencrypted packet</p>
</dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Packet.send"> <dt class="sig sig-object py" id="RNS.Packet.send">
<span class="sig-name descname"><span class="pre">send</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Packet.send" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">send</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Packet.send" title="Permalink to this definition"></a></dt>
@@ -653,8 +725,8 @@ from a the <em>send()</em> method of a <a class="reference internal" href="#api-
</dd></dl> </dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.PacketReceipt.rtt"> <dt class="sig sig-object py" id="RNS.PacketReceipt.get_rtt">
<span class="sig-name descname"><span class="pre">rtt</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.PacketReceipt.rtt" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">get_rtt</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.PacketReceipt.get_rtt" title="Permalink to this definition"></a></dt>
<dd><dl class="field-list simple"> <dd><dl class="field-list simple">
<dt class="field-odd">Returns</dt> <dt class="field-odd">Returns</dt>
<dd class="field-odd"><p>The round-trip-time in seconds</p> <dd class="field-odd"><p>The round-trip-time in seconds</p>
@@ -674,8 +746,8 @@ from a the <em>send()</em> method of a <a class="reference internal" href="#api-
</dd></dl> </dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.PacketReceipt.delivery_callback"> <dt class="sig sig-object py" id="RNS.PacketReceipt.set_delivery_callback">
<span class="sig-name descname"><span class="pre">delivery_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.PacketReceipt.delivery_callback" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">set_delivery_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.PacketReceipt.set_delivery_callback" title="Permalink to this definition"></a></dt>
<dd><p>Sets a function that gets called if a successfull delivery has been proven.</p> <dd><p>Sets a function that gets called if a successfull delivery has been proven.</p>
<dl class="field-list simple"> <dl class="field-list simple">
<dt class="field-odd">Parameters</dt> <dt class="field-odd">Parameters</dt>
@@ -685,8 +757,8 @@ from a the <em>send()</em> method of a <a class="reference internal" href="#api-
</dd></dl> </dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.PacketReceipt.timeout_callback"> <dt class="sig sig-object py" id="RNS.PacketReceipt.set_timeout_callback">
<span class="sig-name descname"><span class="pre">timeout_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.PacketReceipt.timeout_callback" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">set_timeout_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.PacketReceipt.set_timeout_callback" title="Permalink to this definition"></a></dt>
<dd><p>Sets a function that gets called if the delivery times out.</p> <dd><p>Sets a function that gets called if the delivery times out.</p>
<dl class="field-list simple"> <dl class="field-list simple">
<dt class="field-odd">Parameters</dt> <dt class="field-odd">Parameters</dt>
@@ -728,10 +800,39 @@ from a the <em>send()</em> method of a <a class="reference internal" href="#api-
<dl class="py attribute"> <dl class="py attribute">
<dt class="sig sig-object py" id="RNS.Link.KEEPALIVE"> <dt class="sig sig-object py" id="RNS.Link.KEEPALIVE">
<span class="sig-name descname"><span class="pre">KEEPALIVE</span></span><em class="property"> <span class="pre">=</span> <span class="pre">180</span></em><a class="headerlink" href="#RNS.Link.KEEPALIVE" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">KEEPALIVE</span></span><em class="property"> <span class="pre">=</span> <span class="pre">360</span></em><a class="headerlink" href="#RNS.Link.KEEPALIVE" title="Permalink to this definition"></a></dt>
<dd><p>Interval for sending keep-alive packets on established links in seconds.</p> <dd><p>Interval for sending keep-alive packets on established links in seconds.</p>
</dd></dl> </dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.identify">
<span class="sig-name descname"><span class="pre">identify</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">identity</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.identify" title="Permalink to this definition"></a></dt>
<dd><p>Identifies the initiator of the link to the remote peer. This can only happen
once the link has been established, and is carried out over the encrypted link.
The identity is only revealed to the remote peer, and initiator anonymity is
thus preserved. This method can be used for authentication.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><p><strong>identity</strong> An RNS.Identity instance to identify as.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.request">
<span class="sig-name descname"><span class="pre">request</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">path</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">response_callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">failed_callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.request" title="Permalink to this definition"></a></dt>
<dd><p>Sends a request to the remote peer.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>path</strong> The request path.</p></li>
<li><p><strong>response_callback</strong> A function or method with the signature <em>response_callback(request_receipt)</em> to be called when a response is received. See the <a class="reference internal" href="examples.html#example-request"><span class="std std-ref">Request Example</span></a> for more info.</p></li>
<li><p><strong>failed_callback</strong> A function or method with the signature <em>failed_callback(request_receipt)</em> to be called when a request fails. See the <a class="reference internal" href="examples.html#example-request"><span class="std std-ref">Request Example</span></a> for more info.</p></li>
</ul>
</dd>
</dl>
</dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.no_inbound_for"> <dt class="sig sig-object py" id="RNS.Link.no_inbound_for">
<span class="sig-name descname"><span class="pre">no_inbound_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.no_inbound_for" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">no_inbound_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.no_inbound_for" title="Permalink to this definition"></a></dt>
@@ -762,6 +863,16 @@ from a the <em>send()</em> method of a <a class="reference internal" href="#api-
</dl> </dl>
</dd></dl> </dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.get_remote_identity">
<span class="sig-name descname"><span class="pre">get_remote_identity</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.get_remote_identity" title="Permalink to this definition"></a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Returns</dt>
<dd class="field-odd"><p>The identity of the remote peer, if it is known</p>
</dd>
</dl>
</dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.teardown"> <dt class="sig sig-object py" id="RNS.Link.teardown">
<span class="sig-name descname"><span class="pre">teardown</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.teardown" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">teardown</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.teardown" title="Permalink to this definition"></a></dt>
@@ -770,8 +881,8 @@ be used if a new link to the same destination is established.</p>
</dd></dl> </dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.packet_callback"> <dt class="sig sig-object py" id="RNS.Link.set_packet_callback">
<span class="sig-name descname"><span class="pre">packet_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.packet_callback" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">set_packet_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.set_packet_callback" title="Permalink to this definition"></a></dt>
<dd><p>Registers a function to be called when a packet has been <dd><p>Registers a function to be called when a packet has been
received over this link.</p> received over this link.</p>
<dl class="field-list simple"> <dl class="field-list simple">
@@ -782,8 +893,8 @@ received over this link.</p>
</dd></dl> </dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.resource_callback"> <dt class="sig sig-object py" id="RNS.Link.set_resource_callback">
<span class="sig-name descname"><span class="pre">resource_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.resource_callback" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">set_resource_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.set_resource_callback" title="Permalink to this definition"></a></dt>
<dd><p>Registers a function to be called when a resource has been <dd><p>Registers a function to be called when a resource has been
advertised over this link. If the function returns <em>True</em> advertised over this link. If the function returns <em>True</em>
the resource will be accepted. If it returns <em>False</em> it will the resource will be accepted. If it returns <em>False</em> it will
@@ -796,8 +907,8 @@ be ignored.</p>
</dd></dl> </dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.resource_started_callback"> <dt class="sig sig-object py" id="RNS.Link.set_resource_started_callback">
<span class="sig-name descname"><span class="pre">resource_started_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.resource_started_callback" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">set_resource_started_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.set_resource_started_callback" title="Permalink to this definition"></a></dt>
<dd><p>Registers a function to be called when a resource has begun <dd><p>Registers a function to be called when a resource has begun
transferring over this link.</p> transferring over this link.</p>
<dl class="field-list simple"> <dl class="field-list simple">
@@ -808,8 +919,8 @@ transferring over this link.</p>
</dd></dl> </dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.resource_concluded_callback"> <dt class="sig sig-object py" id="RNS.Link.set_resource_concluded_callback">
<span class="sig-name descname"><span class="pre">resource_concluded_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.resource_concluded_callback" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">set_resource_concluded_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.set_resource_concluded_callback" title="Permalink to this definition"></a></dt>
<dd><p>Registers a function to be called when a resource has concluded <dd><p>Registers a function to be called when a resource has concluded
transferring over this link.</p> transferring over this link.</p>
<dl class="field-list simple"> <dl class="field-list simple">
@@ -819,6 +930,18 @@ transferring over this link.</p>
</dl> </dl>
</dd></dl> </dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.set_remote_identified_callback">
<span class="sig-name descname"><span class="pre">set_remote_identified_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.set_remote_identified_callback" title="Permalink to this definition"></a></dt>
<dd><p>Registers a function to be called when an initiating peer has
identified over this link.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><p><strong>callback</strong> A function or method with the signature <em>callback(identity)</em> to be called.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method"> <dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.set_resource_strategy"> <dt class="sig sig-object py" id="RNS.Link.set_resource_strategy">
<span class="sig-name descname"><span class="pre">set_resource_strategy</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">resource_strategy</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.set_resource_strategy" title="Permalink to this definition"></a></dt> <span class="sig-name descname"><span class="pre">set_resource_strategy</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">resource_strategy</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.set_resource_strategy" title="Permalink to this definition"></a></dt>
@@ -852,7 +975,7 @@ client application and throw an error message to the user.</p>
<span id="api-resource"></span><h3>Resource<a class="headerlink" href="#resource" title="Permalink to this headline"></a></h3> <span id="api-resource"></span><h3>Resource<a class="headerlink" href="#resource" title="Permalink to this headline"></a></h3>
<dl class="py class"> <dl class="py class">
<dt class="sig sig-object py" id="RNS.Resource"> <dt class="sig sig-object py" id="RNS.Resource">
<em class="property"><span class="pre">class</span> </em><span class="sig-prename descclassname"><span class="pre">RNS.</span></span><span class="sig-name descname"><span class="pre">Resource</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">data</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">link</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">advertise</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">auto_compress</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">must_compress</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">progress_callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">segment_index</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">1</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">original_hash</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Resource" title="Permalink to this definition"></a></dt> <em class="property"><span class="pre">class</span> </em><span class="sig-prename descclassname"><span class="pre">RNS.</span></span><span class="sig-name descname"><span class="pre">Resource</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">data</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">link</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">advertise</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">auto_compress</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">progress_callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">segment_index</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">1</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">original_hash</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">request_id</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">is_response</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Resource" title="Permalink to this definition"></a></dt>
<dd><p>The Resource class allows transferring arbitrary amounts <dd><p>The Resource class allows transferring arbitrary amounts
of data over a link. It will automatically handle sequencing, of data over a link. It will automatically handle sequencing,
compression, coordination and checksumming.</p> compression, coordination and checksumming.</p>
@@ -863,11 +986,12 @@ compression, coordination and checksumming.</p>
<li><p><strong>link</strong> The <a class="reference internal" href="#api-link"><span class="std std-ref">RNS.Link</span></a> instance on which to transfer the data.</p></li> <li><p><strong>link</strong> The <a class="reference internal" href="#api-link"><span class="std std-ref">RNS.Link</span></a> instance on which to transfer the data.</p></li>
<li><p><strong>advertise</strong> Whether to automatically advertise the resource. Can be <em>True</em> or <em>False</em>.</p></li> <li><p><strong>advertise</strong> Whether to automatically advertise the resource. Can be <em>True</em> or <em>False</em>.</p></li>
<li><p><strong>auto_compress</strong> Whether to auto-compress the resource. Can be <em>True</em> or <em>False</em>.</p></li> <li><p><strong>auto_compress</strong> Whether to auto-compress the resource. Can be <em>True</em> or <em>False</em>.</p></li>
<li><p><strong>auto_compress</strong> Whether the resource must be compressed. Can be <em>True</em> or <em>False</em>. Used for debugging, will disappear in the future.</p></li>
<li><p><strong>callback</strong> A <em>callable</em> with the signature <em>callback(resource)</em>. Will be called when the resource transfer concludes.</p></li> <li><p><strong>callback</strong> A <em>callable</em> with the signature <em>callback(resource)</em>. Will be called when the resource transfer concludes.</p></li>
<li><p><strong>progress_callback</strong> A <em>callable</em> with the signature <em>callback(resource)</em>. Will be called whenever the resource transfer progress is updated.</p></li> <li><p><strong>progress_callback</strong> A <em>callable</em> with the signature <em>callback(resource)</em>. Will be called whenever the resource transfer progress is updated.</p></li>
<li><p><strong>segment_index</strong> Internal use, ignore.</p></li> <li><p><strong>segment_index</strong> Internal use, ignore.</p></li>
<li><p><strong>original_hash</strong> Internal use, ignore.</p></li> <li><p><strong>original_hash</strong> Internal use, ignore.</p></li>
<li><p><strong>is_request</strong> Internal use, ignore.</p></li>
<li><p><strong>is_response</strong> Internal use, ignore.</p></li>
</ul> </ul>
</dd> </dd>
</dl> </dl>
@@ -982,11 +1106,11 @@ will announce it.</p>
</ul> </ul>
<h4>Previous topic</h4> <h4>Previous topic</h4>
<p class="topless"><a href="examples.html"
title="previous chapter">Examples</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="understanding.html" <p class="topless"><a href="understanding.html"
title="next chapter">Understanding Reticulum</a></p> title="previous chapter">Understanding Reticulum</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="examples.html"
title="next chapter">Examples</a></p>
<div role="note" aria-label="source link"> <div role="note" aria-label="source link">
<h3>This Page</h3> <h3>This Page</h3>
<ul class="this-page-menu"> <ul class="this-page-menu">
@@ -1015,12 +1139,12 @@ will announce it.</p>
<a href="genindex.html" title="General Index" <a href="genindex.html" title="General Index"
>index</a></li> >index</a></li>
<li class="right" > <li class="right" >
<a href="understanding.html" title="Understanding Reticulum" <a href="examples.html" title="Examples"
>next</a> |</li> >next</a> |</li>
<li class="right" > <li class="right" >
<a href="examples.html" title="Examples" <a href="understanding.html" title="Understanding Reticulum"
>previous</a> |</li> >previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">API Reference</a></li> <li class="nav-item nav-item-this"><a href="">API Reference</a></li>
</ul> </ul>
</div> </div>
+3 -3
View File
@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; Reticulum Network Stack 0.2.0 beta documentation</title> <title>Search &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" /> <link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -29,7 +29,7 @@
<li class="right" style="margin-right: 10px"> <li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index" <a href="genindex.html" title="General Index"
accesskey="I">index</a></li> accesskey="I">index</a></li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Search</a></li> <li class="nav-item nav-item-this"><a href="">Search</a></li>
</ul> </ul>
</div> </div>
@@ -85,7 +85,7 @@
<li class="right" style="margin-right: 10px"> <li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index" <a href="genindex.html" title="General Index"
>index</a></li> >index</a></li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Search</a></li> <li class="nav-item nav-item-this"><a href="">Search</a></li>
</ul> </ul>
</div> </div>
File diff suppressed because one or more lines are too long
+168 -107
View File
@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Understanding Reticulum &#8212; Reticulum Network Stack 0.2.0 beta documentation</title> <title>Understanding Reticulum &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" /> <link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -16,7 +16,8 @@
<link rel="index" title="Index" href="genindex.html" /> <link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" /> <link rel="search" title="Search" href="search.html" />
<link rel="prev" title="API Reference" href="reference.html" /> <link rel="next" title="API Reference" href="reference.html" />
<link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
</head><body> </head><body>
<div class="related" role="navigation" aria-label="related navigation"> <div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3> <h3>Navigation</h3>
@@ -26,8 +27,11 @@
accesskey="I">index</a></li> accesskey="I">index</a></li>
<li class="right" > <li class="right" >
<a href="reference.html" title="API Reference" <a href="reference.html" title="API Reference"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="gettingstartedfast.html" title="Getting Started Fast"
accesskey="P">previous</a> |</li> accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Understanding Reticulum</a></li> <li class="nav-item nav-item-this"><a href="">Understanding Reticulum</a></li>
</ul> </ul>
</div> </div>
@@ -204,9 +208,11 @@ when a node is directly reachable.</p>
<p>Destinations are created and named in an easy to understand dotted notation of <em>aspects</em>, and <p>Destinations are created and named in an easy to understand dotted notation of <em>aspects</em>, and
represented on the network as a hash of this value. The hash is a SHA-256 truncated to 80 bits. The represented on the network as a hash of this value. The hash is a SHA-256 truncated to 80 bits. The
top level aspect should always be a unique identifier for the application using the destination. top level aspect should always be a unique identifier for the application using the destination.
The next levels of aspects can be defined in any way by the creator of the application. For example, The next levels of aspects can be defined in any way by the creator of the application.</p>
a destination for a environmental monitoring application could be made up of the application name, a <p>Aspects can be as long and as plentiful as required, and a resulting long destination name will not
device type and measurement type, like this:</p> impact efficiency, as names are always represented as truncated SHA-256 hashes on the network.</p>
<p>As an example, a destination for a environmental monitoring application could be made up of the
application name, a device type and measurement type, like this:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>app name : environmentlogger <div class="highlight-text notranslate"><div class="highlight"><pre><span></span>app name : environmentlogger
aspects : remotesensor, temperature aspects : remotesensor, temperature
@@ -242,9 +248,8 @@ receives.</p>
</dl> </dl>
</li> </li>
<li><dl class="simple"> <li><dl class="simple">
<dt><strong>Group</strong></dt><dd><p>When private communication between two or more endpoints is needed. More efficient in <dt><strong>Group</strong></dt><dd><p>When private communication between two or more endpoints is needed. Supports multiple hops
data usage than <em>single</em> destinations. Supports multiple hops indirectly, but must first be indirectly, but must first be established through a <em>single</em> destination.</p>
established through a <em>single</em> destination.</p>
</dd> </dd>
</dl> </dl>
</li> </li>
@@ -260,9 +265,9 @@ nodes aware of your destinations public key, called the <em>announce</em>. It is
an unknown public key from the network, as all participating nodes serve as a distributed ledger an unknown public key from the network, as all participating nodes serve as a distributed ledger
of public keys.</p> of public keys.</p>
<p>Note that public key information can be shared and verified in many other ways than using the <p>Note that public key information can be shared and verified in many other ways than using the
built-in methodology, and that it is therefore not required to use the announce/request functionality. built-in <em>announce</em> functionality, and that it is therefore not required to use the announce/request
It is by far the easiest though, and should definitely be used if there is not a good reason for functionality to obtain public keys. It is by far the easiest though, and should definitely be used
doing it differently.</p> if there is not a good reason for doing it differently.</p>
</div> </div>
</div> </div>
<div class="section" id="public-key-announcements"> <div class="section" id="public-key-announcements">
@@ -278,7 +283,7 @@ contain the following information:</p>
<li><p>The announcers public key</p></li> <li><p>The announcers public key</p></li>
<li><p>Application specific data, in this case the users nickname and availability status</p></li> <li><p>Application specific data, in this case the users nickname and availability status</p></li>
<li><p>A random blob, making each new announce unique</p></li> <li><p>A random blob, making each new announce unique</p></li>
<li><p>A signature of the above information, verifying authenticity</p></li> <li><p>An Ed25519 signature of the above information, verifying authenticity</p></li>
</ul> </ul>
<p>With this information, any Reticulum node that receives it will be able to reconstruct an outgoing <p>With this information, any Reticulum node that receives it will be able to reconstruct an outgoing
destination to securely communicate with that destination. You might have noticed that there is one destination to securely communicate with that destination. You might have noticed that there is one
@@ -286,8 +291,9 @@ piece of information lacking to reconstruct full knowledge of the announced dest
the aspect names of the destination. These are intentionally left out to save bandwidth, since they the aspect names of the destination. These are intentionally left out to save bandwidth, since they
will be implicit in almost all cases. If a destination name is not entirely implicit, information can be will be implicit in almost all cases. If a destination name is not entirely implicit, information can be
included in the application specific data part that will allow the receiver to infer the naming.</p> included in the application specific data part that will allow the receiver to infer the naming.</p>
<p>It is important to note that announcements will be forwarded throughout the network according to a <p>It is important to note that announces will be forwarded throughout the network according to a
certain pattern. This will be detailed later.</p> certain pattern. This will be detailed in the section
<a class="reference internal" href="#understanding-announce"><span class="std std-ref">The Announce Mechanism in Detail</span></a>.</p>
<p>Seeing how <em>single</em> destinations are always tied to a private/public key pair leads us to the next topic.</p> <p>Seeing how <em>single</em> destinations are always tied to a private/public key pair leads us to the next topic.</p>
</div> </div>
<div class="section" id="understanding-identities"> <div class="section" id="understanding-identities">
@@ -304,16 +310,16 @@ automatically. This may be desirable in some situations, but often you will prob
the identity first, and then link it to created destinations.</p> the identity first, and then link it to created destinations.</p>
<p>Building upon the simple messenger example, we could use an identity to represent the user of the <p>Building upon the simple messenger example, we could use an identity to represent the user of the
application. Destinations created will then be linked to this identity to allow communication to application. Destinations created will then be linked to this identity to allow communication to
reach the user. In such a case it is of great importance to store the users identity securely and reach the user. In all cases it is of great importance to store the private keys associated with any
privately.</p> Reticulum Identity securely and privately.</p>
</div> </div>
<div class="section" id="getting-further"> <div class="section" id="getting-further">
<span id="understanding-gettingfurther"></span><h3>Getting Further<a class="headerlink" href="#getting-further" title="Permalink to this headline"></a></h3> <span id="understanding-gettingfurther"></span><h3>Getting Further<a class="headerlink" href="#getting-further" title="Permalink to this headline"></a></h3>
<p>The above functions and principles form the core of Reticulum, and would suffice to create <p>The above functions and principles form the core of Reticulum, and would suffice to create
functional networked applications in local clusters, for example over radio links where all interested functional networked applications in local clusters, for example over radio links where all interested
nodes can directly hear each other. But to be truly useful, we need a way to direct traffic over multiple nodes can directly hear each other. But to be truly useful, we need a way to direct traffic over multiple
hops in the network. In the next sections, two concepts that allow this will be introduced, <em>paths</em> and hops in the network.</p>
<em>links</em>.</p> <p>In the following sections, two concepts that allow this will be introduced, <em>paths</em> and <em>links</em>.</p>
</div> </div>
</div> </div>
<div class="section" id="reticulum-transport"> <div class="section" id="reticulum-transport">
@@ -327,69 +333,20 @@ very limited. Existing routing protocols like BGP or OSPF carry too much overhea
useable over bandwidth-limited, high-latency links.</p> useable over bandwidth-limited, high-latency links.</p>
<p>To overcome such challenges, Reticulums <em>Transport</em> system uses public-key cryptography to <p>To overcome such challenges, Reticulums <em>Transport</em> system uses public-key cryptography to
implement the concept of <em>paths</em> that allow discovery of how to get information to a certain implement the concept of <em>paths</em> that allow discovery of how to get information to a certain
destination, and <em>resources</em> that help make reliable data transfer more efficient.</p> destination. It is important to note that no single node in a Reticulum network knows the complete
<div class="section" id="reaching-the-destination"> path to a destination. Every Transport node participating in a Reticulum network will only
<span id="understanding-paths"></span><h3>Reaching the Destination<a class="headerlink" href="#reaching-the-destination" title="Permalink to this headline"></a></h3> know what the most direct way to get a packet one hop closer to its destination is.</p>
<p>In networks with changing topology and trustless connectivity, nodes need a way to establish <div class="section" id="the-announce-mechanism-in-detail">
<em>verified connectivity</em> with each other. Since the network is assumed to be trustless, Reticulum <span id="understanding-announce"></span><h3>The Announce Mechanism in Detail<a class="headerlink" href="#the-announce-mechanism-in-detail" title="Permalink to this headline"></a></h3>
must provide a way to guarantee that the peer you are communicating with is actually who you <p>When an <em>announce</em> is transmitted by a node, it will be forwarded by any node receiving it, but
expect. To do this, the following process is employed:</p> according to some specific rules:</p>
<ul> <ul>
<li><div class="line-block"> <li><div class="line-block">
<div class="line">First, the node that wishes to establish connectivity will send out a special packet, that <div class="line">If this exact announce has already been received before, ignore it.</div>
traverses the network and locates the desired destination. Along the way, the nodes that
forward the packet will take note of this <em>link request</em>.</div>
</div> </div>
</li> </li>
<li><div class="line-block"> <li><div class="line-block">
<div class="line">Second, if the destination accepts the <em>link request</em> , it will send back a packet that proves the <div class="line">If not, record into a table which node the announce was received from, and how many times in
authenticity of its identity (and the receipt of the link request) to the initiating node. All
nodes that initially forwarded the packet will also be able to verify this proof, and thus
accept the validity of the <em>link</em> throughout the network.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">When the validity of the <em>link</em> has been accepted by forwarding nodes, these nodes will
remember the <em>link</em> , and it can subsequently be used by referring to a hash representing it.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">As a part of the <em>link request</em> , a Diffie-Hellman key exchange takes place, that sets up an
efficient symmetrically encrypted tunnel between the two nodes, using elliptic curve
cryptography. As such, this mode of communication is preferred, even for situations when
nodes can directly communicate, when the amount of data to be exchanged numbers in the
tens of packets.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">When a <em>link</em> has been set up, it automatically provides message receipt functionality, so the
sending node can obtain verified confirmation that the information reached the intended
recipient.</div>
</div>
</li>
</ul>
<p>In a moment, we will discuss the specifics of how this methodology is implemented, but lets first
recap what purposes this serves. We first ensure that the node answering our request is actually the
one we want to communicate with, and not a malicious actor pretending to be so. At the same time
we establish an efficient encrypted channel. The setup of this is relatively cheap in terms of
bandwidth, so it can be used just for a short exchange, and then recreated as needed, which will also
rotate encryption keys, but the link can also be kept alive for longer periods of time, if this is
more suitable to the application. The amount of bandwidth used on keeping a link open is practically
negligible. The procedure also inserts the <em>link id</em> , a hash calculated from the link request packet,
into the memory of forwarding nodes, which means that the communicating nodes can thereafter reach each
other simply by referring to this <em>link id</em>.</p>
<div class="section" id="step-1-pathfinding">
<h4>Step 1: Pathfinding<a class="headerlink" href="#step-1-pathfinding" title="Permalink to this headline"></a></h4>
<p>The pathfinding method builds on the <em>announce</em> functionality discussed earlier. When an announce
is sent out by a node, it will be forwarded by any node receiving it, but according to some specific
rules:</p>
<ul>
<li><div class="line-block">
<div class="line">If this announce has already been received before, ignore it.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">Record into a table which node the announce was received from, and how many times in
total it has been retransmitted to get here.</div> total it has been retransmitted to get here.</div>
</div> </div>
</li> </li>
@@ -399,8 +356,7 @@ set to 18.</div>
</div> </div>
</li> </li>
<li><div class="line-block"> <li><div class="line-block">
<div class="line">The announce will be assigned a delay <em>d</em> = c<sup>h</sup> seconds, where <em>c</em> is a decay constant, by <div class="line">The announce will be assigned a delay <em>d</em> = c<sup>h</sup> seconds, where <em>c</em> is a decay constant, and <em>h</em> is the amount of times this packet has already been forwarded.</div>
default 2, and <em>h</em> is the amount of times this packet has already been forwarded.</div>
</div> </div>
</li> </li>
<li><div class="line-block"> <li><div class="line-block">
@@ -415,10 +371,11 @@ not utilized by other traffic, the announce will be forwarded.</div>
</li> </li>
<li><div class="line-block"> <li><div class="line-block">
<div class="line">If no other nodes are heard retransmitting the announce with a greater hop count than when <div class="line">If no other nodes are heard retransmitting the announce with a greater hop count than when
it left this node, transmitting it will be retried <em>r</em> times. By default, <em>r</em> is set to 2. Retries follow it left this node, transmitting it will be retried <em>r</em> times. By default, <em>r</em> is set to 1. Retries
same rules as above, with the exception that it must wait for at least <em>d</em> = c<sup>h+1</sup> + t seconds, ie., follow same rules as above, with the exception that it must wait for at least <em>d</em> = c<sup>h+1</sup> +
the amount of time it would take the next node to retransmit the packet. By default, <em>t</em> is set to t + rand(0, rw) seconds. This amount of time is equal to the amount of time it would take the next
10.</div> node to retransmit the packet, plus a random window. By default, <em>t</em> is set to 10 seconds, and the
random window <em>rw</em> is set to 10 seconds.</div>
</div> </div>
</li> </li>
<li><div class="line-block"> <li><div class="line-block">
@@ -440,13 +397,111 @@ distance of <em>Lavg =</em> 15 kilometers, an announce will be able to propagate
kilometers in 34 minutes, and a <em>maximum announce radius</em> of 270 kilometers in approximately 3 kilometers in 34 minutes, and a <em>maximum announce radius</em> of 270 kilometers in approximately 3
days.</p> days.</p>
</div> </div>
<div class="section" id="step-2-link-establishment"> <div class="section" id="reaching-the-destination">
<h4>Step 2: Link Establishment<a class="headerlink" href="#step-2-link-establishment" title="Permalink to this headline"></a></h4> <span id="understanding-paths"></span><h3>Reaching the Destination<a class="headerlink" href="#reaching-the-destination" title="Permalink to this headline"></a></h3>
<p>After seeing how the conditions for finding a path through the network are created, we will now <p>In networks with changing topology and trustless connectivity, nodes need a way to establish
explore how two nodes can establish reliable communications over multiple hops. The <em>link</em> in <em>verified connectivity</em> with each other. Since the network is assumed to be trustless, Reticulum
Reticulum terminology should not be viewed as a direct node-to-node link on the physical layer, but must provide a way to guarantee that the peer you are communicating with is actually who you
as an abstract channel, that can be open for any amount of time, and can span an arbitrary number expect. Reticulum offers two ways to do this.</p>
of hops, where information will be exchanged between two nodes.</p> <p>For exchanges of small amounts of information, Reticulum offers the <em>Packet</em> API, which works exactly like you would expect - on a per packet level. The following process is employed when sending a packet:</p>
<ul>
<li><div class="line-block">
<div class="line">A packet is always created with an associated destination and some payload data. When the packet is sent
to a <em>single</em> destination type, Reticulum will automatically create an ephemeral encryption key, perform
an ECDH key exchange with the destinations public key, and encrypt the information.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">It is important to note that this key exchange does not require any network traffic. The sender already
knows the public key of the destination from an earlier received <em>announce</em>, and can thus perform the ECDH
key exchange locally, before sending the packet.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">The public part of the newly generated ephemeral key-pair is included with the encrypted token, and sent
along with the encrypted payload data in the packet.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">When the destination receives the packet, it can itself perform an ECDH key exchange and decrypt the
packet.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">A new ephemeral key is used for every packet sent in this way, and forward secrecy is guaranteed on a
per packet level.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">Once the packet has been received and decrypted by the addressed destination, that destination can opt
to <em>prove</em> its receipt of the packet. It does this by calculating the SHA-256 hash of the received packet,
and signing this hash with its Ed25519 signing key. Transport nodes in the network can then direct this
<em>proof</em> back to the packets origin, where the signature can be verified against the destinations known
public signing key.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">In case the packet is addressed to a <em>group</em> destination type, the packet will be encrypted with the
pre-shared AES-128 key associated with the destination. In case the packet is addressed to a <em>plain</em>
destination type, the payload data will not be encrypted. Neither of these two destination types offer
forward secrecy. In general, it is recommended to always use the <em>single</em> destination type, unless it is
strictly necessary to use one of the others.</div>
</div>
</li>
</ul>
<p>For exchanges of larger amounts of data, or when longer sessions of bidirectional communication is desired, Reticulum offers the <em>Link</em> API. To establish a <em>link</em>, the following process is employed:</p>
<ul>
<li><div class="line-block">
<div class="line">First, the node that wishes to establish a link will send out a special packet, that
traverses the network and locates the desired destination. Along the way, the nodes that
forward the packet will take note of this <em>link request</em>.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">Second, if the destination accepts the <em>link request</em> , it will send back a packet that proves the
authenticity of its identity (and the receipt of the link request) to the initiating node. All
nodes that initially forwarded the packet will also be able to verify this proof, and thus
accept the validity of the <em>link</em> throughout the network.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">When the validity of the <em>link</em> has been accepted by forwarding nodes, these nodes will
remember the <em>link</em> , and it can subsequently be used by referring to a hash representing it.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">As a part of the <em>link request</em> , a Diffie-Hellman key exchange takes place, that sets up an
efficiently encrypted tunnel between the two nodes, using elliptic curve cryptography. As such,
this mode of communication is preferred, even for situations when nodes can directly communicate,
when the amount of data to be exchanged numbers in the tens of packets.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">When a <em>link</em> has been set up, it automatically provides message receipt functionality, through
the same <em>proof</em> mechanism discussed before, so the sending node can obtain verified confirmation
that the information reached the intended recipient.</div>
</div>
</li>
</ul>
<p>In a moment, we will discuss the details of how this methodology is implemented, but lets first
recap what purposes this methodology serves. We first ensure that the node answering our request
is actually the one we want to communicate with, and not a malicious actor pretending to be so.
At the same time we establish an efficient encrypted channel. The setup of this is relatively cheap in
terms of bandwidth, so it can be used just for a short exchange, and then recreated as needed, which will
also rotate encryption keys. The link can also be kept alive for longer periods of time, if this is
more suitable to the application. The procedure also inserts the <em>link id</em> , a hash calculated from the link request packet, into the memory of forwarding nodes, which means that the communicating nodes can thereafter reach each other simply by referring to this <em>link id</em>.</p>
<p>The combined bandwidth cost of setting up a link is 3 packets totalling 240 bytes (more info in the
<a class="reference internal" href="#understanding-packetformat"><span class="std std-ref">Binary Packet Format</span></a> section). The amount of bandwidth used on keeping
a link open is practically negligible, at 0.62 bits per second. Even on a slow 1200 bits per second packet
radio channel, 100 concurrent links will still leave 95% channel capacity for actual data.</p>
<div class="section" id="link-establishment-in-detail">
<h4>Link Establishment in Detail<a class="headerlink" href="#link-establishment-in-detail" title="Permalink to this headline"></a></h4>
<p>After exploring the basics of the announce mechanism, finding a path through the network, and an overview
of the link establishment procedure, this section will go into greater detail about the Reticulum link
establishment process.</p>
<p>The <em>link</em> in Reticulum terminology should not be viewed as a direct node-to-node link on the
physical layer, but as an abstract channel, that can be open for any amount of time, and can span
an arbitrary number of hops, where information will be exchanged between two nodes.</p>
<ul> <ul>
<li><div class="line-block"> <li><div class="line-block">
<div class="line">When a node in the network wants to establish verified connectivity with another node, it <div class="line">When a node in the network wants to establish verified connectivity with another node, it
@@ -461,8 +516,7 @@ considered as single public key for simplicity in this explanation.</em></div>
</li> </li>
<li><div class="line-block"> <li><div class="line-block">
<div class="line">The <em>link request</em> is addressed to the destination hash of the desired destination, and <div class="line">The <em>link request</em> is addressed to the destination hash of the desired destination, and
contains the following data: The newly generated X25519 public key <em>LKi</em>. The contents contains the following data: The newly generated X25519 public key <em>LKi</em>.</div>
are encrypted with the RSA public key of the destination and tramsitted over the network.</div>
</div> </div>
</li> </li>
<li><div class="line-block"> <li><div class="line-block">
@@ -473,21 +527,22 @@ previously.</div>
<li><div class="line-block"> <li><div class="line-block">
<div class="line">Any node that forwards the link request will store a <em>link id</em> in its <em>link table</em> , along with the <div class="line">Any node that forwards the link request will store a <em>link id</em> in its <em>link table</em> , along with the
amount of hops the packet had taken when received. The link id is a hash of the entire link amount of hops the packet had taken when received. The link id is a hash of the entire link
request packet. If the path is not <em>proven</em> within some set amount of time, the entry will be request packet. If the link request packet is not <em>proven</em> by the addressed destination within some
dropped from the <em>link table</em> again.</div> set amount of time, the entry will be dropped from the <em>link table</em> again.</div>
</div> </div>
</li> </li>
<li><div class="line-block"> <li><div class="line-block">
<div class="line">When the destination receives the link request packet, it will decrypt it and decide whether to <div class="line">When the destination receives the link request packet, it will decide whether to accept the request.
accept the request. If it is accepted, the destination will also generate a new X25519 private/public If it is accepted, the destination will also generate a new X25519 private/public key pair, and
key pair, and perform a Diffie Hellman Key Exchange, deriving a new symmetric key that will be used perform a Diffie Hellman Key Exchange, deriving a new symmetric key that will be used to encrypt the
to encrypt the channel, once it has been established.</div> channel, once it has been established.</div>
</div> </div>
</li> </li>
<li><div class="line-block"> <li><div class="line-block">
<div class="line">A <em>link proof</em> packet is now constructed and transmitted over the network. This packet is <div class="line">A <em>link proof</em> packet is now constructed and transmitted over the network. This packet is
addressed to the <em>link id</em> of the <em>link</em>. It contains the following data: The newly generated X25519 addressed to the <em>link id</em> of the <em>link</em>. It contains the following data: The newly generated X25519
public key <em>LKr</em> and an RSA-1024 signature of the <em>link id</em> and <em>LKr</em>.</div> public key <em>LKr</em> and an Ed25519 signature of the <em>link id</em> and <em>LKr</em> made by the signing key of
the addressed destination.</div>
</div> </div>
</li> </li>
<li><div class="line-block"> <li><div class="line-block">
@@ -615,7 +670,7 @@ the light of Reticulums goal of equal access, doing so would need to be the subj
investigation of the consequences first.</p> investigation of the consequences first.</p>
</div> </div>
<div class="section" id="binary-packet-format"> <div class="section" id="binary-packet-format">
<h3>Binary Packet Format<a class="headerlink" href="#binary-packet-format" title="Permalink to this headline"></a></h3> <span id="understanding-packetformat"></span><h3>Binary Packet Format<a class="headerlink" href="#binary-packet-format" title="Permalink to this headline"></a></h3>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>== Reticulum Wire Format ====== <div class="highlight-text notranslate"><div class="highlight"><pre><span></span>== Reticulum Wire Format ======
A Reticulum packet is composed of the following fields: A Reticulum packet is composed of the following fields:
@@ -706,9 +761,9 @@ proof 11
wire size including all fields. wire size including all fields.
- Path Request : 33 bytes - Path Request : 33 bytes
- Announce : 323 bytes - Announce : 151 bytes
- Link Request : 141 bytes - Link Request : 77 bytes
- Link Proof : 205 bytes - Link Proof : 77 bytes
- Link RTT packet : 86 bytes - Link RTT packet : 86 bytes
- Link keepalive : 14 bytes - Link keepalive : 14 bytes
</pre></div> </pre></div>
@@ -740,9 +795,9 @@ proof 11
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#reticulum-transport">Reticulum Transport</a><ul> <li><a class="reference internal" href="#reticulum-transport">Reticulum Transport</a><ul>
<li><a class="reference internal" href="#the-announce-mechanism-in-detail">The Announce Mechanism in Detail</a></li>
<li><a class="reference internal" href="#reaching-the-destination">Reaching the Destination</a><ul> <li><a class="reference internal" href="#reaching-the-destination">Reaching the Destination</a><ul>
<li><a class="reference internal" href="#step-1-pathfinding">Step 1: Pathfinding</a></li> <li><a class="reference internal" href="#link-establishment-in-detail">Link Establishment in Detail</a></li>
<li><a class="reference internal" href="#step-2-link-establishment">Step 2: Link Establishment</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#resources">Resources</a></li> <li><a class="reference internal" href="#resources">Resources</a></li>
@@ -760,8 +815,11 @@ proof 11
</ul> </ul>
<h4>Previous topic</h4> <h4>Previous topic</h4>
<p class="topless"><a href="gettingstartedfast.html"
title="previous chapter">Getting Started Fast</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="reference.html" <p class="topless"><a href="reference.html"
title="previous chapter">API Reference</a></p> title="next chapter">API Reference</a></p>
<div role="note" aria-label="source link"> <div role="note" aria-label="source link">
<h3>This Page</h3> <h3>This Page</h3>
<ul class="this-page-menu"> <ul class="this-page-menu">
@@ -791,8 +849,11 @@ proof 11
>index</a></li> >index</a></li>
<li class="right" > <li class="right" >
<a href="reference.html" title="API Reference" <a href="reference.html" title="API Reference"
>next</a> |</li>
<li class="right" >
<a href="gettingstartedfast.html" title="Getting Started Fast"
>previous</a> |</li> >previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Understanding Reticulum</a></li> <li class="nav-item nav-item-this"><a href="">Understanding Reticulum</a></li>
</ul> </ul>
</div> </div>
+17 -9
View File
@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>What is Reticulum? &#8212; Reticulum Network Stack 0.2.0 beta documentation</title> <title>What is Reticulum? &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" /> <link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -31,7 +31,7 @@
<li class="right" > <li class="right" >
<a href="index.html" title="Reticulum Network Stack Manual" <a href="index.html" title="Reticulum Network Stack Manual"
accesskey="P">previous</a> |</li> accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">What is Reticulum?</a></li> <li class="nav-item nav-item-this"><a href="">What is Reticulum?</a></li>
</ul> </ul>
</div> </div>
@@ -43,7 +43,8 @@
<div class="section" id="what-is-reticulum"> <div class="section" id="what-is-reticulum">
<h1>What is Reticulum?<a class="headerlink" href="#what-is-reticulum" title="Permalink to this headline"></a></h1> <h1>What is Reticulum?<a class="headerlink" href="#what-is-reticulum" title="Permalink to this headline"></a></h1>
<p>Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth. Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.</p> <p>Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth.</p>
<p>Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.</p>
<p>Reticulum is a complete networking stack, and does not use IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks. Reticulum is built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.</p> <p>Reticulum is a complete networking stack, and does not use IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks. Reticulum is built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.</p>
<p>No kernel modules or drivers are required. Reticulum runs completely in userland, and can run on practically any system that runs Python 3.</p> <p>No kernel modules or drivers are required. Reticulum runs completely in userland, and can run on practically any system that runs Python 3.</p>
<div class="section" id="current-status"> <div class="section" id="current-status">
@@ -59,18 +60,19 @@
<ul class="simple"> <ul class="simple">
<li><p>Coordination-less globally unique adressing and identification</p></li> <li><p>Coordination-less globally unique adressing and identification</p></li>
<li><p>Fully self-configuring multi-hop routing</p></li> <li><p>Fully self-configuring multi-hop routing</p></li>
<li><p>Asymmetric RSA encryption and signatures as basis for all communication</p></li> <li><p>Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication</p></li>
<li><p>Perfect Forward Secrecy on links with ephemereal Elliptic Curve Diffie-Hellman keys (on Curve25519)</p></li> <li><p>Forward Secrecy with ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519</p></li>
<li><p>Reticulum uses the Fernet specification for encryption on links and to group destinations</p> <li><p>Reticulum uses the <a class="reference external" href="https://github.com/fernet/spec/blob/master/Spec.md">Fernet</a> specification for encryption</p>
<ul> <ul>
<li><p>AES-128 in CBC mode with PKCS7 padding</p></li> <li><p>AES-128 in CBC mode with PKCS7 padding</p></li>
<li><p>HMAC using SHA256 for authentication</p></li> <li><p>HMAC using SHA256 for authentication</p></li>
<li><p>IVs are generated through os.urandom()</p></li> <li><p>IVs are generated through os.urandom()</p></li>
<li><p>Keys are ephemeral and derived from an ECDH key exchange on Curve25519</p></li>
</ul> </ul>
</li> </li>
<li><p>Unforgeable packet delivery confirmations</p></li> <li><p>Unforgeable packet delivery confirmations</p></li>
<li><p>A variety of supported interface types</p></li> <li><p>A variety of supported interface types</p></li>
<li><p>An intuitive and easy-to-use API</p></li> <li><p>An intuitive and developer-friendly API</p></li>
<li><p>Reliable and efficient transfer of arbritrary amounts of data</p> <li><p>Reliable and efficient transfer of arbritrary amounts of data</p>
<ul> <ul>
<li><p>Reticulum can handle a few bytes of data or files of many gigabytes</p></li> <li><p>Reticulum can handle a few bytes of data or files of many gigabytes</p></li>
@@ -78,11 +80,17 @@
<li><p>The API is very easy to use, and provides transfer progress</p></li> <li><p>The API is very easy to use, and provides transfer progress</p></li>
</ul> </ul>
</li> </li>
<li><p>Efficient link establishment</p>
<ul>
<li><p>Total bandwidth cost of setting up a link is only 3 packets, totalling 240 bytes</p></li>
<li><p>Low cost of keeping links open at only 0.62 bits per second</p></li>
</ul>
</li>
</ul> </ul>
</div> </div>
<div class="section" id="where-can-reticulum-be-used"> <div class="section" id="where-can-reticulum-be-used">
<h2>Where can Reticulum be Used?<a class="headerlink" href="#where-can-reticulum-be-used" title="Permalink to this headline"></a></h2> <h2>Where can Reticulum be Used?<a class="headerlink" href="#where-can-reticulum-be-used" title="Permalink to this headline"></a></h2>
<p>On practically any hardware that can support at least a half-duplex channel <p>Over practically any medium that can support at least a half-duplex channel
with 1.000 bits per second throughput, and an MTU of 500 bytes. Data radios, with 1.000 bits per second throughput, and an MTU of 500 bytes. Data radios,
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes, modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
ad-hoc WiFi, free-space optical links and similar systems are all examples ad-hoc WiFi, free-space optical links and similar systems are all examples
@@ -174,7 +182,7 @@ network, and vice versa.</p>
<li class="right" > <li class="right" >
<a href="index.html" title="Reticulum Network Stack Manual" <a href="index.html" title="Reticulum Network Stack Manual"
>previous</a> |</li> >previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.0 beta documentation</a> &#187;</li> <li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">What is Reticulum?</a></li> <li class="nav-item nav-item-this"><a href="">What is Reticulum?</a></li>
</ul> </ul>
</div> </div>
+1 -1
View File
@@ -22,7 +22,7 @@ copyright = '2021, Mark Qvist'
author = 'Mark Qvist' author = 'Mark Qvist'
# The full version, including alpha/beta/rc tags # The full version, including alpha/beta/rc tags
release = '0.2.0 beta' release = '0.2.2 beta'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
+23
View File
@@ -68,6 +68,29 @@ destination, and passing traffic back and forth over the link.
This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py>`_. This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py>`_.
.. _example-identify:
Identification
==============
The *Identify* example explores identifying an intiator of a link, once
the link has been established.
.. literalinclude:: ../../Examples/Identify.py
This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Identify.py>`_.
.. _example-request:
Requests & Responses
====================
The *Request* example explores sendig requests and receiving responses.
.. literalinclude:: ../../Examples/Request.py
This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Request.py>`_.
.. _example-filetransfer: .. _example-filetransfer:
Filetransfer Filetransfer
+1 -2
View File
@@ -1,7 +1,6 @@
******************** ********************
Getting Started Fast Getting Started Fast
******************** ********************
What do we want to do? Something! When do we want to do it? Right now! Let's go.
The best way to get started with the Reticulum Network Stack depends on what The best way to get started with the Reticulum Network Stack depends on what
you want to do. This guide will outline sensible starting paths for different you want to do. This guide will outline sensible starting paths for different
@@ -23,7 +22,7 @@ in the development for the messaging and information-sharing protocol
Develop a Program with Reticulum Develop a Program with Reticulum
=========================================== ===========================================
If you want to develop programs that use Reticulum, the easiest way to get If you want to develop programs that use Reticulum, the easiest way to get
started is to install Reticulum via pip: started is to install the latest release of Reticulum via pip:
.. code:: .. code::
+2 -2
View File
@@ -10,9 +10,9 @@ the development of Reticulum itself.
whatis whatis
gettingstartedfast gettingstartedfast
examples
reference
understanding understanding
reference
examples
Indices and Tables Indices and Tables
+139 -92
View File
@@ -160,9 +160,13 @@ Destination Naming
Destinations are created and named in an easy to understand dotted notation of *aspects*, and Destinations are created and named in an easy to understand dotted notation of *aspects*, and
represented on the network as a hash of this value. The hash is a SHA-256 truncated to 80 bits. The represented on the network as a hash of this value. The hash is a SHA-256 truncated to 80 bits. The
top level aspect should always be a unique identifier for the application using the destination. top level aspect should always be a unique identifier for the application using the destination.
The next levels of aspects can be defined in any way by the creator of the application. For example, The next levels of aspects can be defined in any way by the creator of the application.
a destination for a environmental monitoring application could be made up of the application name, a
device type and measurement type, like this: Aspects can be as long and as plentiful as required, and a resulting long destination name will not
impact efficiency, as names are always represented as truncated SHA-256 hashes on the network.
As an example, a destination for a environmental monitoring application could be made up of the
application name, a device type and measurement type, like this:
.. code-block:: text .. code-block:: text
@@ -201,9 +205,8 @@ To recap, the different destination types should be used in the following situat
* **Single** * **Single**
When private communication between two endpoints is needed. Supports multiple hops. When private communication between two endpoints is needed. Supports multiple hops.
* **Group** * **Group**
When private communication between two or more endpoints is needed. More efficient in When private communication between two or more endpoints is needed. Supports multiple hops
data usage than *single* destinations. Supports multiple hops indirectly, but must first be indirectly, but must first be established through a *single* destination.
established through a *single* destination.
* **Plain** * **Plain**
When plain-text communication is desirable, for example when broadcasting information. When plain-text communication is desirable, for example when broadcasting information.
@@ -214,9 +217,9 @@ an unknown public key from the network, as all participating nodes serve as a di
of public keys. of public keys.
Note that public key information can be shared and verified in many other ways than using the Note that public key information can be shared and verified in many other ways than using the
built-in methodology, and that it is therefore not required to use the announce/request functionality. built-in *announce* functionality, and that it is therefore not required to use the announce/request
It is by far the easiest though, and should definitely be used if there is not a good reason for functionality to obtain public keys. It is by far the easiest though, and should definitely be used
doing it differently. if there is not a good reason for doing it differently.
.. _understanding-keyannouncements: .. _understanding-keyannouncements:
@@ -235,7 +238,7 @@ contain the following information:
* The announcers public key * The announcers public key
* Application specific data, in this case the users nickname and availability status * Application specific data, in this case the users nickname and availability status
* A random blob, making each new announce unique * A random blob, making each new announce unique
* A signature of the above information, verifying authenticity * An Ed25519 signature of the above information, verifying authenticity
With this information, any Reticulum node that receives it will be able to reconstruct an outgoing With this information, any Reticulum node that receives it will be able to reconstruct an outgoing
destination to securely communicate with that destination. You might have noticed that there is one destination to securely communicate with that destination. You might have noticed that there is one
@@ -244,8 +247,9 @@ the aspect names of the destination. These are intentionally left out to save ba
will be implicit in almost all cases. If a destination name is not entirely implicit, information can be will be implicit in almost all cases. If a destination name is not entirely implicit, information can be
included in the application specific data part that will allow the receiver to infer the naming. included in the application specific data part that will allow the receiver to infer the naming.
It is important to note that announcements will be forwarded throughout the network according to a It is important to note that announces will be forwarded throughout the network according to a
certain pattern. This will be detailed later. certain pattern. This will be detailed in the section
:ref:`The Announce Mechanism in Detail<understanding-announce>`.
Seeing how *single* destinations are always tied to a private/public key pair leads us to the next topic. Seeing how *single* destinations are always tied to a private/public key pair leads us to the next topic.
@@ -268,8 +272,8 @@ the identity first, and then link it to created destinations.
Building upon the simple messenger example, we could use an identity to represent the user of the Building upon the simple messenger example, we could use an identity to represent the user of the
application. Destinations created will then be linked to this identity to allow communication to application. Destinations created will then be linked to this identity to allow communication to
reach the user. In such a case it is of great importance to store the users identity securely and reach the user. In all cases it is of great importance to store the private keys associated with any
privately. Reticulum Identity securely and privately.
.. _understanding-gettingfurther: .. _understanding-gettingfurther:
@@ -279,8 +283,9 @@ Getting Further
The above functions and principles form the core of Reticulum, and would suffice to create The above functions and principles form the core of Reticulum, and would suffice to create
functional networked applications in local clusters, for example over radio links where all interested functional networked applications in local clusters, for example over radio links where all interested
nodes can directly hear each other. But to be truly useful, we need a way to direct traffic over multiple nodes can directly hear each other. But to be truly useful, we need a way to direct traffic over multiple
hops in the network. In the next sections, two concepts that allow this will be introduced, *paths* and hops in the network.
*links*.
In the following sections, two concepts that allow this will be introduced, *paths* and *links*.
.. _understanding-transport: .. _understanding-transport:
@@ -298,70 +303,28 @@ useable over bandwidth-limited, high-latency links.
To overcome such challenges, Reticulums *Transport* system uses public-key cryptography to To overcome such challenges, Reticulums *Transport* system uses public-key cryptography to
implement the concept of *paths* that allow discovery of how to get information to a certain implement the concept of *paths* that allow discovery of how to get information to a certain
destination, and *resources* that help make reliable data transfer more efficient. destination. It is important to note that no single node in a Reticulum network knows the complete
path to a destination. Every Transport node participating in a Reticulum network will only
know what the most direct way to get a packet one hop closer to it's destination is.
.. _understanding-paths: .. _understanding-announce:
Reaching the Destination The Announce Mechanism in Detail
------------------------ --------------------------------
In networks with changing topology and trustless connectivity, nodes need a way to establish When an *announce* is transmitted by a node, it will be forwarded by any node receiving it, but
*verified connectivity* with each other. Since the network is assumed to be trustless, Reticulum according to some specific rules:
must provide a way to guarantee that the peer you are communicating with is actually who you
expect. To do this, the following process is employed:
* | First, the node that wishes to establish connectivity will send out a special packet, that * | If this exact announce has already been received before, ignore it.
traverses the network and locates the desired destination. Along the way, the nodes that
forward the packet will take note of this *link request*.
* | Second, if the destination accepts the *link request* , it will send back a packet that proves the * | If not, record into a table which node the announce was received from, and how many times in
authenticity of its identity (and the receipt of the link request) to the initiating node. All
nodes that initially forwarded the packet will also be able to verify this proof, and thus
accept the validity of the *link* throughout the network.
* | When the validity of the *link* has been accepted by forwarding nodes, these nodes will
remember the *link* , and it can subsequently be used by referring to a hash representing it.
* | As a part of the *link request* , a Diffie-Hellman key exchange takes place, that sets up an
efficient symmetrically encrypted tunnel between the two nodes, using elliptic curve
cryptography. As such, this mode of communication is preferred, even for situations when
nodes can directly communicate, when the amount of data to be exchanged numbers in the
tens of packets.
* | When a *link* has been set up, it automatically provides message receipt functionality, so the
sending node can obtain verified confirmation that the information reached the intended
recipient.
In a moment, we will discuss the specifics of how this methodology is implemented, but lets first
recap what purposes this serves. We first ensure that the node answering our request is actually the
one we want to communicate with, and not a malicious actor pretending to be so. At the same time
we establish an efficient encrypted channel. The setup of this is relatively cheap in terms of
bandwidth, so it can be used just for a short exchange, and then recreated as needed, which will also
rotate encryption keys, but the link can also be kept alive for longer periods of time, if this is
more suitable to the application. The amount of bandwidth used on keeping a link open is practically
negligible. The procedure also inserts the *link id* , a hash calculated from the link request packet,
into the memory of forwarding nodes, which means that the communicating nodes can thereafter reach each
other simply by referring to this *link id*.
Step 1: Pathfinding
^^^^^^^^^^^^^^^^^^^
The pathfinding method builds on the *announce* functionality discussed earlier. When an announce
is sent out by a node, it will be forwarded by any node receiving it, but according to some specific
rules:
* | If this announce has already been received before, ignore it.
* | Record into a table which node the announce was received from, and how many times in
total it has been retransmitted to get here. total it has been retransmitted to get here.
* | If the announce has been retransmitted *m+1* times, it will not be forwarded. By default, *m* is * | If the announce has been retransmitted *m+1* times, it will not be forwarded. By default, *m* is
set to 18. set to 18.
* | The announce will be assigned a delay *d* = c\ :sup:`h` seconds, where *c* is a decay constant, by * | The announce will be assigned a delay *d* = c\ :sup:`h` seconds, where *c* is a decay constant, and *h* is the amount of times this packet has already been forwarded.
default 2, and *h* is the amount of times this packet has already been forwarded.
* | The packet will be given a priority *p = 1/d*. * | The packet will be given a priority *p = 1/d*.
@@ -370,10 +333,11 @@ rules:
not utilized by other traffic, the announce will be forwarded. not utilized by other traffic, the announce will be forwarded.
* | If no other nodes are heard retransmitting the announce with a greater hop count than when * | If no other nodes are heard retransmitting the announce with a greater hop count than when
it left this node, transmitting it will be retried *r* times. By default, *r* is set to 2. Retries follow it left this node, transmitting it will be retried *r* times. By default, *r* is set to 1. Retries
same rules as above, with the exception that it must wait for at least *d* = c\ :sup:`h+1` + t seconds, ie., follow same rules as above, with the exception that it must wait for at least *d* = c\ :sup:`h+1` +
the amount of time it would take the next node to retransmit the packet. By default, *t* is set to t + rand(0, rw) seconds. This amount of time is equal to the amount of time it would take the next
10. node to retransmit the packet, plus a random window. By default, *t* is set to 10 seconds, and the
random window *rw* is set to 10 seconds.
* | If a newer announce from the same destination arrives, while an identical one is already in * | If a newer announce from the same destination arrives, while an identical one is already in
the queue, the newest announce is discarded. If the newest announce contains different the queue, the newest announce is discarded. If the newest announce contains different
@@ -392,14 +356,95 @@ distance of *Lavg =* 15 kilometers, an announce will be able to propagate outwar
kilometers in 34 minutes, and a *maximum announce radius* of 270 kilometers in approximately 3 kilometers in 34 minutes, and a *maximum announce radius* of 270 kilometers in approximately 3
days. days.
Step 2: Link Establishment .. _understanding-paths:
^^^^^^^^^^^^^^^^^^^^^^^^^^
After seeing how the conditions for finding a path through the network are created, we will now Reaching the Destination
explore how two nodes can establish reliable communications over multiple hops. The *link* in ------------------------
Reticulum terminology should not be viewed as a direct node-to-node link on the physical layer, but
as an abstract channel, that can be open for any amount of time, and can span an arbitrary number In networks with changing topology and trustless connectivity, nodes need a way to establish
of hops, where information will be exchanged between two nodes. *verified connectivity* with each other. Since the network is assumed to be trustless, Reticulum
must provide a way to guarantee that the peer you are communicating with is actually who you
expect. Reticulum offers two ways to do this.
For exchanges of small amounts of information, Reticulum offers the *Packet* API, which works exactly like you would expect - on a per packet level. The following process is employed when sending a packet:
* | A packet is always created with an associated destination and some payload data. When the packet is sent
to a *single* destination type, Reticulum will automatically create an ephemeral encryption key, perform
an ECDH key exchange with the destinations public key, and encrypt the information.
* | It is important to note that this key exchange does not require any network traffic. The sender already
knows the public key of the destination from an earlier received *announce*, and can thus perform the ECDH
key exchange locally, before sending the packet.
* | The public part of the newly generated ephemeral key-pair is included with the encrypted token, and sent
along with the encrypted payload data in the packet.
* | When the destination receives the packet, it can itself perform an ECDH key exchange and decrypt the
packet.
* | A new ephemeral key is used for every packet sent in this way, and forward secrecy is guaranteed on a
per packet level.
* | Once the packet has been received and decrypted by the addressed destination, that destination can opt
to *prove* its receipt of the packet. It does this by calculating the SHA-256 hash of the received packet,
and signing this hash with it's Ed25519 signing key. Transport nodes in the network can then direct this
*proof* back to the packets origin, where the signature can be verified against the destinations known
public signing key.
* | In case the packet is addressed to a *group* destination type, the packet will be encrypted with the
pre-shared AES-128 key associated with the destination. In case the packet is addressed to a *plain*
destination type, the payload data will not be encrypted. Neither of these two destination types offer
forward secrecy. In general, it is recommended to always use the *single* destination type, unless it is
strictly necessary to use one of the others.
For exchanges of larger amounts of data, or when longer sessions of bidirectional communication is desired, Reticulum offers the *Link* API. To establish a *link*, the following process is employed:
* | First, the node that wishes to establish a link will send out a special packet, that
traverses the network and locates the desired destination. Along the way, the nodes that
forward the packet will take note of this *link request*.
* | Second, if the destination accepts the *link request* , it will send back a packet that proves the
authenticity of its identity (and the receipt of the link request) to the initiating node. All
nodes that initially forwarded the packet will also be able to verify this proof, and thus
accept the validity of the *link* throughout the network.
* | When the validity of the *link* has been accepted by forwarding nodes, these nodes will
remember the *link* , and it can subsequently be used by referring to a hash representing it.
* | As a part of the *link request* , a Diffie-Hellman key exchange takes place, that sets up an
efficiently encrypted tunnel between the two nodes, using elliptic curve cryptography. As such,
this mode of communication is preferred, even for situations when nodes can directly communicate,
when the amount of data to be exchanged numbers in the tens of packets.
* | When a *link* has been set up, it automatically provides message receipt functionality, through
the same *proof* mechanism discussed before, so the sending node can obtain verified confirmation
that the information reached the intended recipient.
In a moment, we will discuss the details of how this methodology is implemented, but lets first
recap what purposes this methodology serves. We first ensure that the node answering our request
is actually the one we want to communicate with, and not a malicious actor pretending to be so.
At the same time we establish an efficient encrypted channel. The setup of this is relatively cheap in
terms of bandwidth, so it can be used just for a short exchange, and then recreated as needed, which will
also rotate encryption keys. The link can also be kept alive for longer periods of time, if this is
more suitable to the application. The procedure also inserts the *link id* , a hash calculated from the link request packet, into the memory of forwarding nodes, which means that the communicating nodes can thereafter reach each other simply by referring to this *link id*.
The combined bandwidth cost of setting up a link is 3 packets totalling 240 bytes (more info in the
:ref:`Binary Packet Format<understanding-packetformat>` section). The amount of bandwidth used on keeping
a link open is practically negligible, at 0.62 bits per second. Even on a slow 1200 bits per second packet
radio channel, 100 concurrent links will still leave 95% channel capacity for actual data.
Link Establishment in Detail
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
After exploring the basics of the announce mechanism, finding a path through the network, and an overview
of the link establishment procedure, this section will go into greater detail about the Reticulum link
establishment process.
The *link* in Reticulum terminology should not be viewed as a direct node-to-node link on the
physical layer, but as an abstract channel, that can be open for any amount of time, and can span
an arbitrary number of hops, where information will be exchanged between two nodes.
* | When a node in the network wants to establish verified connectivity with another node, it * | When a node in the network wants to establish verified connectivity with another node, it
@@ -412,25 +457,25 @@ of hops, where information will be exchanged between two nodes.
considered as single public key for simplicity in this explanation.* considered as single public key for simplicity in this explanation.*
* | The *link request* is addressed to the destination hash of the desired destination, and * | The *link request* is addressed to the destination hash of the desired destination, and
contains the following data: The newly generated X25519 public key *LKi*. The contents contains the following data: The newly generated X25519 public key *LKi*.
are encrypted with the RSA public key of the destination and tramsitted over the network.
* | The broadcasted packet will be directed through the network according to the rules laid out * | The broadcasted packet will be directed through the network according to the rules laid out
previously. previously.
* | Any node that forwards the link request will store a *link id* in its *link table* , along with the * | Any node that forwards the link request will store a *link id* in its *link table* , along with the
amount of hops the packet had taken when received. The link id is a hash of the entire link amount of hops the packet had taken when received. The link id is a hash of the entire link
request packet. If the path is not *proven* within some set amount of time, the entry will be request packet. If the link request packet is not *proven* by the addressed destination within some
dropped from the *link table* again. set amount of time, the entry will be dropped from the *link table* again.
* | When the destination receives the link request packet, it will decrypt it and decide whether to * | When the destination receives the link request packet, it will decide whether to accept the request.
accept the request. If it is accepted, the destination will also generate a new X25519 private/public If it is accepted, the destination will also generate a new X25519 private/public key pair, and
key pair, and perform a Diffie Hellman Key Exchange, deriving a new symmetric key that will be used perform a Diffie Hellman Key Exchange, deriving a new symmetric key that will be used to encrypt the
to encrypt the channel, once it has been established. channel, once it has been established.
* | A *link proof* packet is now constructed and transmitted over the network. This packet is * | A *link proof* packet is now constructed and transmitted over the network. This packet is
addressed to the *link id* of the *link*. It contains the following data: The newly generated X25519 addressed to the *link id* of the *link*. It contains the following data: The newly generated X25519
public key *LKr* and an RSA-1024 signature of the *link id* and *LKr*. public key *LKr* and an Ed25519 signature of the *link id* and *LKr* made by the signing key of
the addressed destination.
* | By verifying this *link proof* packet, all nodes that originally transported the *link request* * | By verifying this *link proof* packet, all nodes that originally transported the *link request*
packet to the destination from the originator can now verify that the intended destination received packet to the destination from the originator can now verify that the intended destination received
@@ -556,6 +601,8 @@ the light of Reticulums goal of equal access, doing so would need to be the subj
investigation of the consequences first. investigation of the consequences first.
.. _understanding-packetformat:
Binary Packet Format Binary Packet Format
-------------------- --------------------
@@ -651,8 +698,8 @@ Binary Packet Format
wire size including all fields. wire size including all fields.
- Path Request : 33 bytes - Path Request : 33 bytes
- Announce : 323 bytes - Announce : 151 bytes
- Link Request : 141 bytes - Link Request : 77 bytes
- Link Proof : 205 bytes - Link Proof : 77 bytes
- Link RTT packet : 86 bytes - Link RTT packet : 86 bytes
- Link keepalive : 14 bytes - Link keepalive : 14 bytes
+16 -6
View File
@@ -2,7 +2,9 @@
What is Reticulum? What is Reticulum?
****************** ******************
Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth. Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more. Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth.
Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.
Reticulum is a complete networking stack, and does not use IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks. Reticulum is built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks. Reticulum is a complete networking stack, and does not use IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks. Reticulum is built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.
@@ -25,11 +27,11 @@ What does Reticulum Offer?
* Fully self-configuring multi-hop routing * Fully self-configuring multi-hop routing
* Asymmetric RSA encryption and signatures as basis for all communication * Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication
* Perfect Forward Secrecy on links with ephemereal Elliptic Curve Diffie-Hellman keys (on Curve25519) * Forward Secrecy with ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519
* Reticulum uses the Fernet specification for encryption on links and to group destinations * Reticulum uses the `Fernet <https://github.com/fernet/spec/blob/master/Spec.md>`_ specification for encryption
* AES-128 in CBC mode with PKCS7 padding * AES-128 in CBC mode with PKCS7 padding
@@ -37,11 +39,13 @@ What does Reticulum Offer?
* IVs are generated through os.urandom() * IVs are generated through os.urandom()
* Keys are ephemeral and derived from an ECDH key exchange on Curve25519
* Unforgeable packet delivery confirmations * Unforgeable packet delivery confirmations
* A variety of supported interface types * A variety of supported interface types
* An intuitive and easy-to-use API * An intuitive and developer-friendly API
* Reliable and efficient transfer of arbritrary amounts of data * Reliable and efficient transfer of arbritrary amounts of data
@@ -51,10 +55,16 @@ What does Reticulum Offer?
* The API is very easy to use, and provides transfer progress * The API is very easy to use, and provides transfer progress
* Efficient link establishment
* Total bandwidth cost of setting up a link is only 3 packets, totalling 240 bytes
* Low cost of keeping links open at only 0.62 bits per second
Where can Reticulum be Used? Where can Reticulum be Used?
============================ ============================
On practically any hardware that can support at least a half-duplex channel Over practically any medium that can support at least a half-duplex channel
with 1.000 bits per second throughput, and an MTU of 500 bytes. Data radios, with 1.000 bits per second throughput, and an MTU of 500 bytes. Data radios,
modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes, modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes,
ad-hoc WiFi, free-space optical links and similar systems are all examples ad-hoc WiFi, free-space optical links and similar systems are all examples
+4 -2
View File
@@ -1,11 +1,13 @@
import setuptools import setuptools
exec(open("RNS/_version.py", "r").read())
with open("README.md", "r") as fh: with open("README.md", "r") as fh:
long_description = fh.read() long_description = fh.read()
setuptools.setup( setuptools.setup(
name="rns", name="rns",
version="0.2.0", version=__version__,
author="Mark Qvist", author="Mark Qvist",
author_email="mark@unsigned.io", author_email="mark@unsigned.io",
description="Self-configuring, encrypted and resilient mesh networking stack for LoRa, packet radio, WiFi and everything in between", description="Self-configuring, encrypted and resilient mesh networking stack for LoRa, packet radio, WiFi and everything in between",
@@ -18,6 +20,6 @@ setuptools.setup(
"License :: OSI Approved :: MIT License", "License :: OSI Approved :: MIT License",
"Operating System :: OS Independent", "Operating System :: OS Independent",
], ],
install_requires=['cryptography>=3.4.7', 'pyserial'], install_requires=['cryptography>=3.4.7', 'pyserial', 'netifaces>=0.10.4'],
python_requires='>=3.5', python_requires='>=3.5',
) )