Compare commits

...

15 Commits

Author SHA1 Message Date
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
35 changed files with 1004 additions and 721 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
# the destination receives data.
broadcast_destination.packet_callback(packet_callback)
broadcast_destination.set_packet_callback(packet_callback)
# Everything's ready!
# 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
# run when a packet is received. We do this so we can
# 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!
# Let's Wait for client requests or user input
@@ -170,12 +170,12 @@ def client(destination_hexhash, configpath, timeout=None):
# the packet times out.
if timeout != None:
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.
# This will get automatically called when a proof for
# 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
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.
def packet_delivered(receipt):
if receipt.status == RNS.PacketReceipt.DELIVERED:
rtt = receipt.rtt()
rtt = receipt.get_rtt()
if (rtt >= 1):
rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds"
+10 -10
View File
@@ -65,7 +65,7 @@ def server(configpath, path):
# We configure a function that will get called every time
# 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!
# Let's Wait for client requests or user input
@@ -102,7 +102,7 @@ def client_connected(link):
if os.path.isdir(serve_path):
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
data = umsgpack.packb(list_files())
@@ -114,8 +114,8 @@ def client_connected(link):
list_packet = RNS.Packet(link, data)
list_receipt = list_packet.send()
list_receipt.set_timeout(APP_TIMEOUT)
list_receipt.delivery_callback(list_delivered)
list_receipt.timeout_callback(list_timeout)
list_receipt.set_delivery_callback(list_delivered)
list_receipt.set_timeout_callback(list_timeout)
else:
RNS.log("Too many files in served directory!", RNS.LOG_ERROR)
RNS.log("You should implement a function to split the filelist over multiple packets.", RNS.LOG_ERROR)
@@ -125,7 +125,7 @@ def client_connected(link):
# open until the client requests a file. We'll
# configure a function that get's called when
# the client sends a packet with a file request.
link.packet_callback(client_request)
link.set_packet_callback(client_request)
else:
RNS.log("Client connected, but served path no longer exists!", RNS.LOG_ERROR)
link.teardown()
@@ -254,18 +254,18 @@ def client(destination_hexhash, configpath):
# We expect any normal data packets on the link
# to contain a list of served files, so we set
# a callback accordingly
link.packet_callback(filelist_received)
link.set_packet_callback(filelist_received)
# We'll also set up functions to inform the
# user when the link is established or closed
link.link_established_callback(link_established)
link.link_closed_callback(link_closed)
link.set_link_established_callback(link_established)
link.set_link_closed_callback(link_closed)
# And set the link to automatically begin
# downloading advertised resources
link.set_resource_strategy(RNS.Link.ACCEPT_ALL)
link.resource_started_callback(download_began)
link.resource_concluded_callback(download_concluded)
link.set_resource_started_callback(download_began)
link.set_resource_concluded_callback(download_concluded)
menu()
+17 -7
View File
@@ -44,7 +44,7 @@ def server(configpath):
# We configure a function that will get called every time
# 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!
# Let's Wait for client requests or user input
@@ -76,8 +76,8 @@ def client_connected(link):
global latest_client_link
RNS.log("Client connected")
link.link_closed_callback(client_disconnected)
link.packet_callback(server_packet_received)
link.set_link_closed_callback(client_disconnected)
link.set_packet_callback(server_packet_received)
latest_client_link = link
def client_disconnected(link):
@@ -149,12 +149,12 @@ def client(destination_hexhash, configpath):
# We set a callback that will get executed
# every time a packet is received over the
# link
link.packet_callback(client_packet_received)
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.link_established_callback(link_established)
link.link_closed_callback(link_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
@@ -181,8 +181,18 @@ def client_loop():
# If not, send the entered text over the link
if text != "":
data = text.encode("utf-8")
RNS.Packet(server_link, data).send()
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()
+8 -4
View File
@@ -18,12 +18,13 @@ For more info, see [unsigned.io/projects/reticulum](https://unsigned.io/projects
## Notable Features
- Coordination-less globally unique adressing and identification
- Fully self-configuring multi-hop routing
- Asymmetric RSA encryption and signatures as basis for all communication
- Perfect Forward Secrecy on links 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
- Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication
- 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
- AES-128 in CBC mode with PKCS7 padding
- HMAC using SHA256 for authentication
- IVs are generated through os.urandom()
- Keys are ephemeral and derived from an ECDH key exchange on Curve25519
- Unforgeable packet delivery confirmations
- A variety of supported interface types
- An intuitive and easy-to-use API
@@ -31,9 +32,12 @@ 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
- Sequencing, transfer coordination and checksumming is automatic
- The API is very easy to use, and provides transfer progress
- 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?
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.
+18 -26
View File
@@ -5,9 +5,6 @@ import RNS
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
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:
def __init__(self):
@@ -31,9 +28,6 @@ class Destination:
:param \*aspects: Any non-zero number of string arguments.
"""
KEYSIZE = RNS.Identity.KEYSIZE;
PADDINGSIZE= RNS.Identity.PADDINGSIZE;
# Constants
SINGLE = 0x00
GROUP = 0x01
@@ -139,8 +133,8 @@ class Destination:
def announce(self, app_data=None, path_response=False):
"""
Creates an announce packet for this destination and broadcasts it on
all interfaces. Application specific data can be added to the announce.
Creates an announce packet for this destination and broadcasts it on all
relevant interfaces. Application specific data can be added to the announce.
:param app_data: *bytes* containing the app_data.
:param path_response: Internal flag used by :ref:`RNS.Transport<api-transport>`. Ignore.
@@ -162,10 +156,7 @@ class Destination:
signature = self.identity.sign(signed_data)
# TODO: Check if this could be optimised by only
# carrying the hash in the destination field, not
# also redundantly inside the signed blob as here
announce_data = self.hash+self.identity.get_public_key()+random_hash+signature
announce_data = self.identity.get_public_key()+random_hash+signature
if app_data != None:
announce_data += app_data
@@ -178,7 +169,7 @@ class Destination:
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
this destination.
@@ -187,7 +178,7 @@ class Destination:
"""
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
this destination.
@@ -196,7 +187,7 @@ class Destination:
"""
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
a packet sent to this destination. Allows control over when and if
@@ -218,14 +209,15 @@ class Destination:
self.proof_strategy = proof_strategy
def receive(self, packet):
plaintext = self.decrypt(packet.data)
if plaintext != None:
if packet.packet_type == RNS.Packet.LINKREQUEST:
self.incoming_link_request(plaintext, packet)
if packet.packet_type == RNS.Packet.DATA:
if self.callbacks.packet != None:
self.callbacks.packet(plaintext, packet)
if packet.packet_type == RNS.Packet.LINKREQUEST:
plaintext = packet.data
self.incoming_link_request(plaintext, packet)
else:
plaintext = self.decrypt(packet.data)
if plaintext != None:
if packet.packet_type == RNS.Packet.DATA:
if self.callbacks.packet != None:
self.callbacks.packet(plaintext, packet)
def incoming_link_request(self, data, packet):
link = RNS.Link.validate_request(self, data, packet)
@@ -245,8 +237,8 @@ class Destination:
raise TypeError("A single destination holds keys through an Identity instance")
if self.type == Destination.GROUP:
self.prv_bytes = Fernet.generate_key()
self.prv = Fernet(self.prv_bytes)
self.prv_bytes = base64.urlsafe_b64decode(Fernet.generate_key())
self.prv = Fernet(base64.urlsafe_b64encode(self.prv_bytes))
def get_private_key(self):
@@ -278,7 +270,7 @@ class Destination:
if self.type == Destination.GROUP:
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):
if self.type != Destination.SINGLE:
+172 -128
View File
@@ -4,14 +4,15 @@ import os
import RNS
import time
import atexit
import base64
from .vendor import umsgpack as umsgpack
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.serialization import load_der_public_key
from cryptography.hazmat.primitives.serialization import load_der_private_key
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.fernet import Fernet
class Identity:
"""
@@ -19,26 +20,29 @@ class Identity:
for encryption, decryption, signatures and verification, and is the basis
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
"""
DERKEYSIZE = KEYSIZE+272
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.
"""
# Non-configurable constants
PADDINGSIZE = 336 # In bits
HASHLENGTH = 256 # In bits
SIGLENGTH = KEYSIZE
ENCRYPT_CHUNKSIZE = (KEYSIZE-PADDINGSIZE)//8
DECRYPT_CHUNKSIZE = KEYSIZE//8
AES_HMAC_OVERHEAD = 58 # In bytes
AES128_BLOCKSIZE = 16 # In bytes
HASHLENGTH = 256 # In bits
SIGLENGTH = KEYSIZE # In bits
TRUNCATED_HASHLENGTH = 80 # In bits
"""
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
@@ -46,7 +50,10 @@ class Identity:
@staticmethod
def remember(packet_hash, destination_hash, public_key, app_data = None):
Identity.known_destinations[destination_hash] = [time.time(), packet_hash, public_key, app_data]
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]
@staticmethod
@@ -60,7 +67,7 @@ class Identity:
RNS.log("Searching for "+RNS.prettyhexrep(destination_hash)+"...", RNS.LOG_EXTREME)
if destination_hash in Identity.known_destinations:
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.app_data = identity_data[3]
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:
RNS.log("Validating announce from "+RNS.prettyhexrep(packet.destination_hash), RNS.LOG_DEBUG)
destination_hash = packet.destination_hash
public_key = packet.data[10:Identity.DERKEYSIZE//8+10]
random_hash = packet.data[Identity.DERKEYSIZE//8+10:Identity.DERKEYSIZE//8+20]
signature = packet.data[Identity.DERKEYSIZE//8+20:Identity.DERKEYSIZE//8+20+Identity.KEYSIZE//8]
public_key = packet.data[:Identity.KEYSIZE//8]
random_hash = packet.data[Identity.KEYSIZE//8:Identity.KEYSIZE//8+10]
signature = packet.data[Identity.KEYSIZE//8+10:Identity.KEYSIZE//8+10+Identity.KEYSIZE//8]
app_data = b""
if len(packet.data) > Identity.DERKEYSIZE//8+20+Identity.KEYSIZE//8:
app_data = 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.KEYSIZE//8+10+Identity.KEYSIZE//8:]
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
announced_identity = Identity(public_only=True)
announced_identity = Identity(create_keys=False)
announced_identity.load_public_key(public_key)
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
: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):
return identity
else:
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
self.prv = None
self.pub = None
self.prv_bytes = None
self.pub_bytes = None
self.hash = None
self.hexhash = None
self.prv = None
self.prv_bytes = None
self.sig_prv = None
self.sig_prv_bytes = None
if not public_only:
self.pub = None
self.pub_bytes = None
self.sig_pub = None
self.sig_pub_bytes = None
self.hash = None
self.hexhash = None
if create_keys:
self.create_keys()
def create_keys(self):
self.prv = rsa.generate_private_key(
public_exponent=65537,
key_size=Identity.KEYSIZE,
backend=default_backend()
)
self.prv_bytes = self.prv.private_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PrivateFormat.PKCS8,
self.prv = X25519PrivateKey.generate()
self.prv_bytes = self.prv.private_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PrivateFormat.Raw,
encryption_algorithm=serialization.NoEncryption()
)
self.pub = self.prv.public_key()
self.pub_bytes = self.pub.public_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PublicFormat.SubjectPublicKeyInfo
self.sig_prv = Ed25519PrivateKey.generate()
self.sig_prv_bytes = self.sig_prv.private_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PrivateFormat.Raw,
encryption_algorithm=serialization.NoEncryption()
)
self.pub = self.prv.public_key()
self.pub_bytes = self.pub.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
self.sig_pub = self.sig_prv.public_key()
self.sig_pub_bytes = self.sig_pub.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
self.update_hashes()
@@ -228,13 +266,13 @@ class Identity:
"""
:returns: The private key as *bytes*
"""
return self.prv_bytes
return self.prv_bytes+self.sig_prv_bytes
def get_public_key(self):
"""
: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):
"""
@@ -244,42 +282,53 @@ class Identity:
:returns: True if the key was loaded, otherwise False.
"""
try:
self.prv_bytes = prv_bytes
self.prv = serialization.load_der_private_key(
self.prv_bytes,
password=None,
backend=default_backend()
self.prv_bytes = prv_bytes[:Identity.KEYSIZE//8//2]
self.prv = X25519PrivateKey.from_private_bytes(self.prv_bytes)
self.sig_prv_bytes = prv_bytes[Identity.KEYSIZE//8//2:]
self.sig_prv = Ed25519PrivateKey.from_private_bytes(self.sig_prv_bytes)
self.pub = self.prv.public_key()
self.pub_bytes = self.pub.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
self.pub = self.prv.public_key()
self.pub_bytes = self.pub.public_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PublicFormat.SubjectPublicKeyInfo
self.sig_pub = self.sig_prv.public_key()
self.sig_pub_bytes = self.sig_pub.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
self.update_hashes()
return True
except Exception as e:
raise e
RNS.log("Failed to load identity key", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
return False
def load_public_key(self, key):
def load_public_key(self, pub_bytes):
"""
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.
"""
try:
self.pub_bytes = key
self.pub = load_der_public_key(self.pub_bytes, backend=default_backend())
self.pub_bytes = pub_bytes[:Identity.KEYSIZE//8//2]
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()
except Exception as e:
RNS.log("Error while loading public key, the contained exception was: "+str(e), RNS.LOG_ERROR)
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()
def to_file(self, path):
@@ -293,7 +342,7 @@ class Identity:
"""
try:
with open(path, "wb") as key_file:
key_file.write(self.prv_bytes)
key_file.write(self.get_public_key())
return True
return False
except Exception as e:
@@ -310,71 +359,78 @@ class Identity:
RNS.log("Error while loading identity from "+str(path), RNS.LOG_ERROR)
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):
"""
Encrypts information for the identity.
:param plaintext: The plaintext to be encrypted as *bytes*.
:returns: Ciphertext as *bytes*.
:raises: *KeyError* if the instance does not hold a public key
:returns: Ciphertext token as *bytes*.
:raises: *KeyError* if the instance does not hold a public key.
"""
if self.pub != None:
chunksize = Identity.ENCRYPT_CHUNKSIZE
chunks = int(math.ceil(len(plaintext)/(float(chunksize))))
ephemeral_key = X25519PrivateKey.generate()
ephemeral_pub_bytes = ephemeral_key.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
ciphertext = b"";
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:
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.
:param ciphertext: The ciphertext to be decrypted as *bytes*.
: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:
plaintext = None
try:
chunksize = Identity.DECRYPT_CHUNKSIZE
chunks = int(math.ceil(len(ciphertext)/(float(chunksize))))
if len(ciphertext_token) > Identity.KEYSIZE//8//2:
plaintext = None
try:
peer_pub_bytes = ciphertext_token[:Identity.KEYSIZE//8//2]
peer_pub = X25519PublicKey.from_public_bytes(peer_pub_bytes)
plaintext = b"";
for chunk in range(chunks):
start = chunk*chunksize
end = (chunk+1)*chunksize
if (chunk+1)*chunksize > len(ciphertext):
end = len(ciphertext)
shared_key = self.prv.exchange(peer_pub)
derived_key = derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=self.get_salt(),
info=self.get_context(),
).derive(shared_key)
plaintext += self.prv.decrypt(
ciphertext[start:end],
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(),
label=None
)
)
except:
RNS.log("Decryption by "+RNS.prettyhexrep(self.hash)+" failed", RNS.LOG_VERBOSE)
return plaintext;
fernet = Fernet(base64.urlsafe_b64encode(derived_key))
ciphertext = ciphertext_token[Identity.KEYSIZE//8//2:]
plaintext = fernet.decrypt(base64.urlsafe_b64encode(ciphertext))
except Exception as e:
RNS.log("Decryption by "+RNS.prettyhexrep(self.hash)+" failed: "+str(e), RNS.LOG_DEBUG)
return plaintext;
else:
RNS.log("Decryption failed because the token size was invalid.", RNS.LOG_DEBUG)
return None
else:
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*.
: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:
signature = self.prv.sign(
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
return signature
if self.sig_prv != None:
try:
return self.sig_prv.sign(message)
except Exception as e:
RNS.log("The identity "+str(self)+" could not sign the requested message. The contained exception was: "+str(e), RNS.LOG_ERROR)
raise e
else:
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 message: The message to be validated as *bytes*.
: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:
try:
self.pub.verify(
signature,
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
self.sig_pub.verify(signature, message)
return True
except Exception as e:
return False
+25 -32
View File
@@ -33,17 +33,15 @@ class Link:
:param peer_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
"""
ECPUBSIZE = 32+32
BLOCKSIZE = 16
KEYSIZE = 32
ECPUBSIZE = 32+32
KEYSIZE = 32
AES_HMAC_OVERHEAD = 58
MDU = math.floor((RNS.Reticulum.MDU-AES_HMAC_OVERHEAD)/BLOCKSIZE)*BLOCKSIZE - 1
MDU = math.floor((RNS.Reticulum.MDU-RNS.Identity.AES_HMAC_OVERHEAD)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1
# TODO: This should not be hardcoded,
# but calculated from something like
@@ -54,7 +52,7 @@ class Link:
"""
TIMEOUT_FACTOR = 3
STALE_GRACE = 2
KEEPALIVE = 180
KEEPALIVE = 360
"""
Interval for sending keep-alive packets on established links in seconds.
"""
@@ -89,11 +87,6 @@ class Link:
RNS.Transport.register_link(link)
link.last_inbound = time.time()
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)
return link
@@ -135,14 +128,15 @@ class Link:
self.__encryption_disabled = False
if self.destination == None:
self.initiator = False
self.prv = self.owner.identity.prv
self.sig_prv = self.owner.identity.sig_prv
else:
self.initiator = True
self.prv = X25519PrivateKey.generate()
self.sig_prv = Ed25519PrivateKey.generate()
self.fernet = None
self.prv = X25519PrivateKey.generate()
self.sig_prv = Ed25519PrivateKey.generate()
self.pub = self.prv.public_key()
self.pub_bytes = self.pub.public_bytes(
encoding=serialization.Encoding.Raw,
@@ -162,10 +156,14 @@ class Link:
self.load_peer(peer_pub_bytes, peer_sig_pub_bytes)
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.packet = RNS.Packet(destination, self.request_data, packet_type=RNS.Packet.LINKREQUEST)
self.packet.pack()
self.set_link_id(self.packet)
self.load_peer(peer_pub_bytes, peer_sig_pub_bytes)
self.handshake()
RNS.Transport.register_link(self)
self.request_time = time.time()
self.start_watchdog()
@@ -202,7 +200,7 @@ class Link:
signed_data = self.link_id+self.pub_bytes+self.sig_pub_bytes
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.send()
self.had_outbound()
@@ -221,15 +219,11 @@ class Link:
self.had_outbound()
def validate_proof(self, packet):
if self.initiator and len(packet.data) == Link.ECPUBSIZE+RNS.Identity.KEYSIZE//8:
peer_pub_bytes = packet.data[:Link.ECPUBSIZE//2]
peer_sig_pub_bytes = packet.data[Link.ECPUBSIZE//2:Link.ECPUBSIZE]
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.initiator and len(packet.data) == RNS.Identity.SIGLENGTH//8:
signed_data = self.link_id+self.peer_pub_bytes+self.peer_sig_pub_bytes
signature = packet.data[:RNS.Identity.SIGLENGTH//8]
if self.destination.identity.validate(signature, signed_data):
self.load_peer(peer_pub_bytes, peer_sig_pub_bytes)
self.handshake()
self.rtt = time.time() - self.request_time
self.attached_interface = packet.receiving_interface
RNS.Transport.activate_link(self)
@@ -265,8 +259,7 @@ class Link:
if self.owner.callbacks.link_established != None:
self.owner.callbacks.link_established(self)
except Exception as e:
RNS.log("Error occurred while processing RTT packet, tearing down link", RNS.LOG_ERROR)
traceback.print_exc()
RNS.log("Error occurred while processing RTT packet, tearing down link. The contained exception was: "+str(e), RNS.LOG_ERROR)
self.teardown()
def get_salt(self):
@@ -537,13 +530,13 @@ class Link:
except Exception as e:
return False
def link_established_callback(self, callback):
def set_link_established_callback(self, callback):
self.callbacks.link_established = callback
def link_closed_callback(self, callback):
def set_link_closed_callback(self, 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
received over this link.
@@ -552,7 +545,7 @@ class Link:
"""
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
advertised over this link. If the function returns *True*
@@ -563,7 +556,7 @@ class Link:
"""
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
transferring over this link.
@@ -572,7 +565,7 @@ class Link:
"""
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
transferring over this link.
+30 -11
View File
@@ -6,8 +6,16 @@ import RNS
class Packet:
"""
The Packet class is used to create packet instances that can be
sent over a Reticulum network.
The Packet class is used to create packet instances that can be sent
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 data: The data payload to be included in the packet as *bytes*.
@@ -56,14 +64,21 @@ class Packet:
# This is used to calculate allowable
# payload sizes
HEADER_MAXSIZE = 23
HEADER_MAXSIZE = RNS.Reticulum.HEADER_MAXSIZE
MDU = RNS.Reticulum.MDU
# With an MTU of 500, the maximum RSA-encrypted
# amount of data we can send in a single packet
# is given by the below calculation; 258 bytes.
RSA_MDU = math.floor(MDU/RNS.Identity.DECRYPT_CHUNKSIZE)*RNS.Identity.ENCRYPT_CHUNKSIZE
PLAIN_MDU = MDU
# TODO: Update this
# With an MTU of 500, the maximum of data we can
# send in a single encrypted packet is given by
# the below calculation; 383 bytes.
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
"""
The maximum size of the payload data in a single unencrypted packet
"""
# TODO: This should be calculated
# more intelligently
@@ -128,6 +143,9 @@ class Packet:
if self.packet_type == Packet.ANNOUNCE:
# Announce packets are not encrypted
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:
# Resource proofs are not encrypted
self.ciphertext = self.data
@@ -171,6 +189,7 @@ class Packet:
self.packed = True
self.update_hash()
def unpack(self):
self.flags = self.raw[0]
self.hops = self.raw[1]
@@ -406,7 +425,7 @@ class PacketReceipt:
else:
return False
def rtt(self):
def get_rtt(self):
"""
:returns: The round-trip-time in seconds
"""
@@ -439,7 +458,7 @@ class PacketReceipt:
"""
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.
@@ -449,7 +468,7 @@ class PacketReceipt:
# Set a function that gets called if the
# 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.
+2 -5
View File
@@ -17,7 +17,6 @@ class Resource:
: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 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 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.
@@ -134,7 +133,7 @@ class Resource:
# Create a resource for transmission to a remote destination
# The data passed can be either a bytes-array or a file opened
# 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):
data_size = None
resource_data = None
if hasattr(data, "read"):
@@ -198,7 +197,7 @@ class Resource:
self.uncompressed_data = data
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)
self.compressed_data = bz2.compress(self.uncompressed_data)
RNS.log("Compression completed in "+str(round(time.time()-compression_began, 3))+" seconds", RNS.LOG_DEBUG)
@@ -748,8 +747,6 @@ class Resource:
:returns: The current progress of the resource transfer as a *float* between 0.0 and 1.0.
"""
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.sent_parts
self.progress_total_parts = float(self.grand_total_parts)
+2 -4
View File
@@ -22,8 +22,6 @@ class Transport:
APP_NAME = "rnstransport"
# TODO: Document the addition of random windows
# and max local rebroadcasts.
PATHFINDER_M = 18 # Max hops
PATHFINDER_C = 2.0 # Decay constant
PATHFINDER_R = 1 # Retransmit retries
@@ -108,7 +106,7 @@ class Transport:
# 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.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_hashes.append(Transport.path_request_destination.hash)
@@ -652,7 +650,7 @@ class Transport:
# First, check that the announce is not for a destination
# 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):
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 = []
if packet.destination_hash in Transport.destination_table:
random_blobs = Transport.destination_table[packet.destination_hash][4]
Binary file not shown.
+1 -1
View File
@@ -1,4 +1,4 @@
# 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.
config: 010a10c5bc670583cef4151858e38839
config: 205a0b937612ce08d1a58b1cbb471256
tags: 645f666f9bcd5a90fca523b33c5a78b7
Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

@@ -1,7 +1,6 @@
********************
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
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
===========================================
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::
+2 -2
View File
@@ -10,9 +10,9 @@ the development of Reticulum itself.
whatis
gettingstartedfast
examples
reference
understanding
reference
examples
Indices and Tables
+140 -93
View File
@@ -157,12 +157,16 @@ destinations. Reticulum uses three different basic destination types, and one sp
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
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,
a destination for a environmental monitoring application could be made up of the application name, a
device type and measurement type, like this:
The next levels of aspects can be defined in any way by the creator of the application.
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
@@ -201,9 +205,8 @@ To recap, the different destination types should be used in the following situat
* **Single**
When private communication between two endpoints is needed. Supports multiple hops.
* **Group**
When private communication between two or more endpoints is needed. More efficient in
data usage than *single* destinations. Supports multiple hops indirectly, but must first be
established through a *single* destination.
When private communication between two or more endpoints is needed. Supports multiple hops
indirectly, but must first be established through a *single* destination.
* **Plain**
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.
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.
It is by far the easiest though, and should definitely be used if there is not a good reason for
doing it differently.
built-in *announce* functionality, and that it is therefore not required to use the announce/request
functionality to obtain public keys. It is by far the easiest though, and should definitely be used
if there is not a good reason for doing it differently.
.. _understanding-keyannouncements:
@@ -235,7 +238,7 @@ contain the following information:
* The announcers public key
* Application specific data, in this case the users nickname and availability status
* 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
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
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
certain pattern. This will be detailed later.
It is important to note that announces will be forwarded throughout the network according to a
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.
@@ -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
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
privately.
reach the user. In all cases it is of great importance to store the private keys associated with any
Reticulum Identity securely and privately.
.. _understanding-gettingfurther:
@@ -279,8 +283,9 @@ Getting Further
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
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
*links*.
hops in the network.
In the following sections, two concepts that allow this will be introduced, *paths* and *links*.
.. _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
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
*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. To do this, the following process is employed:
When an *announce* is transmitted by a node, it will be forwarded by any node receiving it, but
according to some specific rules:
* | First, the node that wishes to establish connectivity 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*.
* | If this exact announce has already been received before, ignore it.
* | 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
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
* | If not, record into a table which node the announce was received from, and how many times in
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
set to 18.
* | The announce will be assigned a delay *d* = c\ :sup:`h` seconds, where *c* is a decay constant, by
default 2, and *h* is the amount of times this packet has already been forwarded.
* | 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.
* | 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.
* | 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
same rules as above, with the exception that it must wait for at least *d* = c\ :sup:`h+1` + t seconds, ie.,
the amount of time it would take the next node to retransmit the packet. By default, *t* is set to
10.
it left this node, transmitting it will be retried *r* times. By default, *r* is set to 1. Retries
follow same rules as above, with the exception that it must wait for at least *d* = c\ :sup:`h+1` +
t + rand(0, rw) seconds. This amount of time is equal to the amount of time it would take the next
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
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
days.
Step 2: Link Establishment
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. _understanding-paths:
After seeing how the conditions for finding a path through the network are created, we will now
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
of hops, where information will be exchanged between two nodes.
Reaching the Destination
------------------------
In networks with changing topology and trustless connectivity, nodes need a way to establish
*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
@@ -412,25 +457,25 @@ of hops, where information will be exchanged between two nodes.
considered as single public key for simplicity in this explanation.*
* | 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
are encrypted with the RSA public key of the destination and tramsitted over the network.
contains the following data: The newly generated X25519 public key *LKi*.
* | The broadcasted packet will be directed through the network according to the rules laid out
previously.
* | 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
request packet. If the path is not *proven* within some set amount of time, the entry will be
dropped from the *link table* again.
request packet. If the link request packet is not *proven* by the addressed destination within some
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
accept the request. If it is accepted, the destination will also generate a new X25519 private/public
key pair, and perform a Diffie Hellman Key Exchange, deriving a new symmetric key that will be used
to encrypt the channel, once it has been established.
* | When the destination receives the link request packet, it will decide whether to accept the request.
If it is accepted, the destination will also generate a new X25519 private/public key pair, and
perform a Diffie Hellman Key Exchange, deriving a new symmetric key that will be used to encrypt the
channel, once it has been established.
* | 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
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*
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.
.. _understanding-packetformat:
Binary Packet Format
--------------------
@@ -651,8 +698,8 @@ Binary Packet Format
wire size including all fields.
- Path Request : 33 bytes
- Announce : 323 bytes
- Link Request : 141 bytes
- Link Proof : 205 bytes
- Announce : 151 bytes
- Link Request : 77 bytes
- Link Proof : 77 bytes
- Link RTT packet : 86 bytes
- Link keepalive : 14 bytes
+12 -4
View File
@@ -25,11 +25,11 @@ What does Reticulum Offer?
* 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
@@ -37,6 +37,8 @@ What does Reticulum Offer?
* IVs are generated through os.urandom()
* Keys are ephemeral and derived from an ECDH key exchange on Curve25519
* Unforgeable packet delivery confirmations
* A variety of supported interface types
@@ -51,10 +53,16 @@ What does Reticulum Offer?
* 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?
============================
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,
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
+1 -1
View File
@@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.2.0 beta',
VERSION: '0.2.1 beta',
LANGUAGE: 'None',
COLLAPSE_INDEX: false,
BUILDER: 'html',
+37 -37
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<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.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.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="search" title="Search" href="search.html" />
<link rel="next" title="API Reference" href="reference.html" />
<link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
<link rel="prev" title="API Reference" href="reference.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
@@ -27,11 +26,8 @@
accesskey="I">index</a></li>
<li class="right" >
<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>
<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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Examples</a></li>
</ul>
</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"># 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"># 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"># 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="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"># 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="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">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"># This will get automatically called when a proof for</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="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="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="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="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>
@@ -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"># 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"># 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="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">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_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">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>
@@ -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"># 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">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"># 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">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_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>
@@ -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="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">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">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>
@@ -1110,7 +1116,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"># 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"># Let&#39;s Wait for client requests or user input</span>
@@ -1147,7 +1153,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="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="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 +1165,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_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">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_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_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="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>
@@ -1170,7 +1176,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"># 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="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="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>
@@ -1299,18 +1305,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"># to contain a list of served files, so we set</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"># 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">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_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"># And set the link to automatically begin</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">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_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_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>
@@ -1667,11 +1673,8 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
</ul>
<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"
title="next chapter">API Reference</a></p>
title="previous chapter">API Reference</a></p>
<div role="note" aria-label="source link">
<h3>This Page</h3>
<ul class="this-page-menu">
@@ -1701,11 +1704,8 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
>index</a></li>
<li class="right" >
<a href="reference.html" title="API Reference"
>next</a> |</li>
<li class="right" >
<a href="gettingstartedfast.html" title="Getting Started Fast"
>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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Examples</a></li>
</ul>
</div>
+49 -37
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<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.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -23,7 +23,7 @@
<li class="right" style="margin-right: 10px">
<a href="#" title="General Index"
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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Index</a></li>
</ul>
</div>
@@ -80,8 +80,12 @@
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Destination.create_keys">create_keys() (RNS.Destination method)</a>
</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>
</ul></li>
</ul></td>
</tr></table>
@@ -98,8 +102,6 @@
</li>
</ul></td>
<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>
<li><a href="reference.html#RNS.Destination">Destination (class in RNS)</a>
@@ -119,11 +121,17 @@
</li>
</ul></li>
</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>
<h2 id="F">F</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<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>
</ul></td>
@@ -144,11 +152,13 @@
<li><a href="reference.html#RNS.Identity.get_private_key">(RNS.Identity method)</a>
</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>
</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>
<li><a href="reference.html#RNS.PacketReceipt.get_rtt">get_rtt() (RNS.PacketReceipt method)</a>
</li>
<li><a href="reference.html#RNS.PacketReceipt.get_status">get_status() (RNS.PacketReceipt method)</a>
</li>
@@ -198,16 +208,14 @@
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Link">Link (class in RNS)</a>
</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>
<ul>
<li><a href="reference.html#RNS.Identity.load_private_key">(RNS.Identity method)</a>
</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>
</ul></td>
@@ -230,19 +238,13 @@
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Packet">Packet (class in RNS)</a>
</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>
<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><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>
</ul></td>
</tr></table>
@@ -256,23 +258,15 @@
</li>
<li><a href="reference.html#RNS.Transport.register_announce_handler">register_announce_handler() (RNS.Transport static method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Transport.request_path">request_path() (RNS.Transport static method)</a>
</li>
<li><a href="reference.html#RNS.Packet.resend">resend() (RNS.Packet method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<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><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>
</ul></td>
</tr></table>
@@ -283,14 +277,34 @@
<li><a href="reference.html#RNS.Packet.send">send() (RNS.Packet method)</a>
</li>
<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><a href="reference.html#RNS.Destination.set_proof_strategy">set_proof_strategy() (RNS.Destination method)</a>
</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_resource_callback">set_resource_callback() (RNS.Link method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<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>
<li><a href="reference.html#RNS.PacketReceipt.set_timeout_callback">set_timeout_callback() (RNS.PacketReceipt method)</a>
</li>
<li><a href="reference.html#RNS.Reticulum.should_allow_unencrypted">should_allow_unencrypted() (RNS.Reticulum static method)</a>
</li>
@@ -309,15 +323,13 @@
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<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><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>
</ul></td>
<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>
<li><a href="reference.html#RNS.Identity.truncated_hash">truncated_hash() (RNS.Identity static method)</a>
@@ -363,7 +375,7 @@
<li class="right" style="margin-right: 10px">
<a href="#" title="General Index"
>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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Index</a></li>
</ul>
</div>
+4 -5
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<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.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -31,7 +31,7 @@
<li class="right" >
<a href="whatis.html" title="What is Reticulum?"
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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Getting Started Fast</a></li>
</ul>
</div>
@@ -43,7 +43,6 @@
<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>
<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
you want to do. This guide will outline sensible starting paths for different
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">
<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
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>
</pre></div>
</div>
@@ -167,7 +166,7 @@ dont use pip, but try this recipe:</p>
<li class="right" >
<a href="whatis.html" title="What is Reticulum?"
>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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Getting Started Fast</a></li>
</ul>
</div>
+33 -32
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<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.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -27,7 +27,7 @@
<li class="right" >
<a href="whatis.html" title="What is Reticulum?"
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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Reticulum Network Stack Manual</a></li>
</ul>
</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>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Examples</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="examples.html#announce">Announce</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="examples.html#echo">Echo</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#link">Link</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#filetransfer">Filetransfer</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</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="understanding.html#goals">Goals</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-l3"><a class="reference internal" href="understanding.html#destinations">Destinations</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#public-key-announcements">Public Key Announcements</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>
</li>
<li class="toctree-l1"><a class="reference internal" href="reference.html">API Reference</a><ul>
@@ -81,28 +97,13 @@ the development of Reticulum itself.</p>
</li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</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="understanding.html#goals">Goals</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-l3"><a class="reference internal" href="understanding.html#destinations">Destinations</a></li>
<li class="toctree-l3"><a class="reference internal" href="understanding.html#public-key-announcements">Public Key Announcements</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#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>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Examples</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="examples.html#announce">Announce</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="examples.html#echo">Echo</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#link">Link</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#filetransfer">Filetransfer</a></li>
</ul>
</li>
</ul>
@@ -164,7 +165,7 @@ the development of Reticulum itself.</p>
<li class="right" >
<a href="whatis.html" title="What is Reticulum?"
>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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Reticulum Network Stack Manual</a></li>
</ul>
</div>
Binary file not shown.
+91 -53
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<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.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.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="search" title="Search" href="search.html" />
<link rel="next" title="Understanding Reticulum" href="understanding.html" />
<link rel="prev" title="Examples" href="examples.html" />
<link rel="next" title="Examples" href="examples.html" />
<link rel="prev" title="Understanding Reticulum" href="understanding.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
@@ -26,12 +26,12 @@
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="understanding.html" title="Understanding Reticulum"
<a href="examples.html" title="Examples"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="examples.html" title="Examples"
<a href="understanding.html" title="Understanding Reticulum"
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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">API Reference</a></li>
</ul>
</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>
<dl class="py class">
<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
for encryption, decryption, signatures and verification, and is the basis
for all encrypted communication over Reticulum networks.</p>
<dl class="field-list simple">
<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>
</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">
<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>
<dd><p>RSA key size in bits.</p>
<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>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>
<dl class="py attribute">
<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>
<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>
<dl class="py method">
@@ -224,6 +230,21 @@ Can be used to load previously created and saved identities into Reticulum.</p>
</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">
<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>
@@ -260,11 +281,11 @@ Can be used to load previously created and saved identities into Reticulum.</p>
<dl class="py method">
<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>
<dl class="field-list simple">
<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>
<dt class="field-even">Returns</dt>
<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>
<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>
<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>
</dl>
</dd></dl>
<dl class="py method">
<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>
<dl class="field-list simple">
<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>
<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>
</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>
<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>
</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>
<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>
</dl>
</dd></dl>
@@ -427,8 +448,8 @@ encrypted communication with it.</p>
<dl class="py method">
<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>
<dd><p>Creates an announce packet for this destination and broadcasts it on
all interfaces. Application specific data can be added to the announce.</p>
<dd><p>Creates an announce packet for this destination and broadcasts it on all
relevant interfaces. Application specific data can be added to the announce.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<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>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.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>
<dt class="sig sig-object py" id="RNS.Destination.set_link_established_callback">
<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
this destination.</p>
<dl class="field-list simple">
@@ -452,8 +473,8 @@ this destination.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.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>
<dt class="sig sig-object py" id="RNS.Destination.set_packet_callback">
<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
this destination.</p>
<dl class="field-list simple">
@@ -464,8 +485,8 @@ this destination.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.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>
<dt class="sig sig-object py" id="RNS.Destination.set_proof_requested_callback">
<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
a packet sent to this destination. Allows control over when and if
proofs should be returned for received packets.</p>
@@ -592,8 +613,14 @@ unless other app_data is specified in the <em>announce</em> method.</p>
<dl class="py class">
<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>
<dd><p>The Packet class is used to create packet instances that can be
sent over a Reticulum network.</p>
<dd><p>The Packet class is used to create packet instances that can be sent
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">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><ul class="simple">
@@ -608,6 +635,18 @@ sent over a Reticulum network.</p>
</ul>
</dd>
</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">
<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>
@@ -653,8 +692,8 @@ from a the <em>send()</em> method of a <a class="reference internal" href="#api-
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.PacketReceipt.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>
<dt class="sig sig-object py" id="RNS.PacketReceipt.get_rtt">
<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">
<dt class="field-odd">Returns</dt>
<dd class="field-odd"><p>The round-trip-time in seconds</p>
@@ -674,8 +713,8 @@ from a the <em>send()</em> method of a <a class="reference internal" href="#api-
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.PacketReceipt.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>
<dt class="sig sig-object py" id="RNS.PacketReceipt.set_delivery_callback">
<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>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
@@ -685,8 +724,8 @@ from a the <em>send()</em> method of a <a class="reference internal" href="#api-
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.PacketReceipt.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>
<dt class="sig sig-object py" id="RNS.PacketReceipt.set_timeout_callback">
<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>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
@@ -728,7 +767,7 @@ from a the <em>send()</em> method of a <a class="reference internal" href="#api-
<dl class="py attribute">
<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></dl>
@@ -770,8 +809,8 @@ be used if a new link to the same destination is established.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.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>
<dt class="sig sig-object py" id="RNS.Link.set_packet_callback">
<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
received over this link.</p>
<dl class="field-list simple">
@@ -782,8 +821,8 @@ received over this link.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.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>
<dt class="sig sig-object py" id="RNS.Link.set_resource_callback">
<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
advertised over this link. If the function returns <em>True</em>
the resource will be accepted. If it returns <em>False</em> it will
@@ -796,8 +835,8 @@ be ignored.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.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>
<dt class="sig sig-object py" id="RNS.Link.set_resource_started_callback">
<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
transferring over this link.</p>
<dl class="field-list simple">
@@ -808,8 +847,8 @@ transferring over this link.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.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>
<dt class="sig sig-object py" id="RNS.Link.set_resource_concluded_callback">
<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
transferring over this link.</p>
<dl class="field-list simple">
@@ -852,7 +891,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>
<dl class="py class">
<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><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
of data over a link. It will automatically handle sequencing,
compression, coordination and checksumming.</p>
@@ -863,7 +902,6 @@ 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>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 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>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>
@@ -982,11 +1020,11 @@ will announce it.</p>
</ul>
<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"
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">
<h3>This Page</h3>
<ul class="this-page-menu">
@@ -1015,12 +1053,12 @@ will announce it.</p>
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="understanding.html" title="Understanding Reticulum"
<a href="examples.html" title="Examples"
>next</a> |</li>
<li class="right" >
<a href="examples.html" title="Examples"
<a href="understanding.html" title="Understanding Reticulum"
>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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">API Reference</a></li>
</ul>
</div>
+3 -3
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<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.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -29,7 +29,7 @@
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Search</a></li>
</ul>
</div>
@@ -85,7 +85,7 @@
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Search</a></li>
</ul>
</div>
File diff suppressed because one or more lines are too long
+169 -108
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<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.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.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="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>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
@@ -26,8 +27,11 @@
accesskey="I">index</a></li>
<li class="right" >
<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>
<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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Understanding Reticulum</a></li>
</ul>
</div>
@@ -201,12 +205,14 @@ when a node is directly reachable.</p>
</ul>
<div class="section" id="destination-naming">
<span id="understanding-destinationnaming"></span><h4>Destination Naming<a class="headerlink" href="#destination-naming" title="Permalink to this headline"></a></h4>
<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
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,
a destination for a environmental monitoring application could be made up of the application name, a
device type and measurement type, like this:</p>
The next levels of aspects can be defined in any way by the creator of the application.</p>
<p>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.</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
aspects : remotesensor, temperature
@@ -242,9 +248,8 @@ receives.</p>
</dl>
</li>
<li><dl class="simple">
<dt><strong>Group</strong></dt><dd><p>When private communication between two or more endpoints is needed. More efficient in
data usage than <em>single</em> destinations. Supports multiple hops indirectly, but must first be
established through a <em>single</em> destination.</p>
<dt><strong>Group</strong></dt><dd><p>When private communication between two or more endpoints is needed. Supports multiple hops
indirectly, but must first be established through a <em>single</em> destination.</p>
</dd>
</dl>
</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
of public keys.</p>
<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.
It is by far the easiest though, and should definitely be used if there is not a good reason for
doing it differently.</p>
built-in <em>announce</em> functionality, and that it is therefore not required to use the announce/request
functionality to obtain public keys. It is by far the easiest though, and should definitely be used
if there is not a good reason for doing it differently.</p>
</div>
</div>
<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>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 signature of the above information, verifying authenticity</p></li>
<li><p>An Ed25519 signature of the above information, verifying authenticity</p></li>
</ul>
<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
@@ -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
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>
<p>It is important to note that announcements will be forwarded throughout the network according to a
certain pattern. This will be detailed later.</p>
<p>It is important to note that announces will be forwarded throughout the network according to a
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>
</div>
<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>
<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
reach the user. In such a case it is of great importance to store the users identity securely and
privately.</p>
reach the user. In all cases it is of great importance to store the private keys associated with any
Reticulum Identity securely and privately.</p>
</div>
<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>
<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
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
<em>links</em>.</p>
hops in the network.</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 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>
<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
destination, and <em>resources</em> that help make reliable data transfer more efficient.</p>
<div class="section" id="reaching-the-destination">
<span id="understanding-paths"></span><h3>Reaching the Destination<a class="headerlink" href="#reaching-the-destination" title="Permalink to this headline"></a></h3>
<p>In networks with changing topology and trustless connectivity, nodes need a way to establish
<em>verified connectivity</em> 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. To do this, the following process is employed:</p>
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 its destination is.</p>
<div class="section" id="the-announce-mechanism-in-detail">
<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>
<p>When an <em>announce</em> is transmitted 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">First, the node that wishes to establish connectivity 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 class="line">If this exact announce has already been received before, ignore it.</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
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
<div class="line">If not, 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>
</div>
</li>
@@ -399,8 +356,7 @@ set to 18.</div>
</div>
</li>
<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
default 2, and <em>h</em> is the amount of times this packet has already been forwarded.</div>
<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>
</div>
</li>
<li><div class="line-block">
@@ -415,10 +371,11 @@ not utilized by other traffic, the announce will be forwarded.</div>
</li>
<li><div class="line-block">
<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
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.,
the amount of time it would take the next node to retransmit the packet. By default, <em>t</em> is set to
10.</div>
it left this node, transmitting it will be retried <em>r</em> times. By default, <em>r</em> is set to 1. Retries
follow same rules as above, with the exception that it must wait for at least <em>d</em> = c<sup>h+1</sup> +
t + rand(0, rw) seconds. This amount of time is equal to the amount of time it would take the next
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>
</li>
<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
days.</p>
</div>
<div class="section" id="step-2-link-establishment">
<h4>Step 2: Link Establishment<a class="headerlink" href="#step-2-link-establishment" title="Permalink to this headline"></a></h4>
<p>After seeing how the conditions for finding a path through the network are created, we will now
explore how two nodes can establish reliable communications over multiple hops. 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>
<div class="section" id="reaching-the-destination">
<span id="understanding-paths"></span><h3>Reaching the Destination<a class="headerlink" href="#reaching-the-destination" title="Permalink to this headline"></a></h3>
<p>In networks with changing topology and trustless connectivity, nodes need a way to establish
<em>verified connectivity</em> 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.</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>
<li><div class="line-block">
<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><div class="line-block">
<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
are encrypted with the RSA public key of the destination and tramsitted over the network.</div>
contains the following data: The newly generated X25519 public key <em>LKi</em>.</div>
</div>
</li>
<li><div class="line-block">
@@ -473,21 +527,22 @@ previously.</div>
<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
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
dropped from the <em>link table</em> again.</div>
request packet. If the link request packet is not <em>proven</em> by the addressed destination within some
set amount of time, the entry will be dropped from the <em>link table</em> again.</div>
</div>
</li>
<li><div class="line-block">
<div class="line">When the destination receives the link request packet, it will decrypt it and decide whether to
accept the request. If it is accepted, the destination will also generate a new X25519 private/public
key pair, and perform a Diffie Hellman Key Exchange, deriving a new symmetric key that will be used
to encrypt the channel, once it has been established.</div>
<div class="line">When the destination receives the link request packet, it will decide whether to accept the request.
If it is accepted, the destination will also generate a new X25519 private/public key pair, and
perform a Diffie Hellman Key Exchange, deriving a new symmetric key that will be used to encrypt the
channel, once it has been established.</div>
</div>
</li>
<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
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>
</li>
<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>
</div>
<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 ======
A Reticulum packet is composed of the following fields:
@@ -706,9 +761,9 @@ proof 11
wire size including all fields.
- Path Request : 33 bytes
- Announce : 323 bytes
- Link Request : 141 bytes
- Link Proof : 205 bytes
- Announce : 151 bytes
- Link Request : 77 bytes
- Link Proof : 77 bytes
- Link RTT packet : 86 bytes
- Link keepalive : 14 bytes
</pre></div>
@@ -740,9 +795,9 @@ proof 11
</ul>
</li>
<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="#step-1-pathfinding">Step 1: Pathfinding</a></li>
<li><a class="reference internal" href="#step-2-link-establishment">Step 2: Link Establishment</a></li>
<li><a class="reference internal" href="#link-establishment-in-detail">Link Establishment in Detail</a></li>
</ul>
</li>
<li><a class="reference internal" href="#resources">Resources</a></li>
@@ -760,8 +815,11 @@ proof 11
</ul>
<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"
title="previous chapter">API Reference</a></p>
title="next chapter">API Reference</a></p>
<div role="note" aria-label="source link">
<h3>This Page</h3>
<ul class="this-page-menu">
@@ -791,8 +849,11 @@ proof 11
>index</a></li>
<li class="right" >
<a href="reference.html" title="API Reference"
>next</a> |</li>
<li class="right" >
<a href="gettingstartedfast.html" title="Getting Started Fast"
>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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Understanding Reticulum</a></li>
</ul>
</div>
+14 -7
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<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.1 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -31,7 +31,7 @@
<li class="right" >
<a href="index.html" title="Reticulum Network Stack Manual"
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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">What is Reticulum?</a></li>
</ul>
</div>
@@ -59,13 +59,14 @@
<ul class="simple">
<li><p>Coordination-less globally unique adressing and identification</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>Perfect Forward Secrecy on links 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>Asymmetric X25519 encryption and Ed25519 signatures as a basis for all communication</p></li>
<li><p>Forward Secrecy with ephemereal Elliptic Curve Diffie-Hellman keys on Curve25519</p></li>
<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>
<li><p>AES-128 in CBC mode with PKCS7 padding</p></li>
<li><p>HMAC using SHA256 for authentication</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>
</li>
<li><p>Unforgeable packet delivery confirmations</p></li>
@@ -78,11 +79,17 @@
<li><p>The API is very easy to use, and provides transfer progress</p></li>
</ul>
</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>
</div>
<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>
<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,
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
@@ -174,7 +181,7 @@ network, and vice versa.</p>
<li class="right" >
<a href="index.html" title="Reticulum Network Stack Manual"
>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.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">What is Reticulum?</a></li>
</ul>
</div>
+1 -1
View File
@@ -22,7 +22,7 @@ copyright = '2021, Mark Qvist'
author = 'Mark Qvist'
# The full version, including alpha/beta/rc tags
release = '0.2.0 beta'
release = '0.2.1 beta'
# -- General configuration ---------------------------------------------------
+1 -2
View File
@@ -1,7 +1,6 @@
********************
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
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
===========================================
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::
+2 -2
View File
@@ -10,9 +10,9 @@ the development of Reticulum itself.
whatis
gettingstartedfast
examples
reference
understanding
reference
examples
Indices and Tables
+140 -93
View File
@@ -157,12 +157,16 @@ destinations. Reticulum uses three different basic destination types, and one sp
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
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,
a destination for a environmental monitoring application could be made up of the application name, a
device type and measurement type, like this:
The next levels of aspects can be defined in any way by the creator of the application.
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
@@ -201,9 +205,8 @@ To recap, the different destination types should be used in the following situat
* **Single**
When private communication between two endpoints is needed. Supports multiple hops.
* **Group**
When private communication between two or more endpoints is needed. More efficient in
data usage than *single* destinations. Supports multiple hops indirectly, but must first be
established through a *single* destination.
When private communication between two or more endpoints is needed. Supports multiple hops
indirectly, but must first be established through a *single* destination.
* **Plain**
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.
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.
It is by far the easiest though, and should definitely be used if there is not a good reason for
doing it differently.
built-in *announce* functionality, and that it is therefore not required to use the announce/request
functionality to obtain public keys. It is by far the easiest though, and should definitely be used
if there is not a good reason for doing it differently.
.. _understanding-keyannouncements:
@@ -235,7 +238,7 @@ contain the following information:
* The announcers public key
* Application specific data, in this case the users nickname and availability status
* 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
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
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
certain pattern. This will be detailed later.
It is important to note that announces will be forwarded throughout the network according to a
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.
@@ -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
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
privately.
reach the user. In all cases it is of great importance to store the private keys associated with any
Reticulum Identity securely and privately.
.. _understanding-gettingfurther:
@@ -279,8 +283,9 @@ Getting Further
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
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
*links*.
hops in the network.
In the following sections, two concepts that allow this will be introduced, *paths* and *links*.
.. _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
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
*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. To do this, the following process is employed:
When an *announce* is transmitted by a node, it will be forwarded by any node receiving it, but
according to some specific rules:
* | First, the node that wishes to establish connectivity 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*.
* | If this exact announce has already been received before, ignore it.
* | 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
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
* | If not, record into a table which node the announce was received from, and how many times in
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
set to 18.
* | The announce will be assigned a delay *d* = c\ :sup:`h` seconds, where *c* is a decay constant, by
default 2, and *h* is the amount of times this packet has already been forwarded.
* | 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.
* | 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.
* | 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
same rules as above, with the exception that it must wait for at least *d* = c\ :sup:`h+1` + t seconds, ie.,
the amount of time it would take the next node to retransmit the packet. By default, *t* is set to
10.
it left this node, transmitting it will be retried *r* times. By default, *r* is set to 1. Retries
follow same rules as above, with the exception that it must wait for at least *d* = c\ :sup:`h+1` +
t + rand(0, rw) seconds. This amount of time is equal to the amount of time it would take the next
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
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
days.
Step 2: Link Establishment
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. _understanding-paths:
After seeing how the conditions for finding a path through the network are created, we will now
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
of hops, where information will be exchanged between two nodes.
Reaching the Destination
------------------------
In networks with changing topology and trustless connectivity, nodes need a way to establish
*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
@@ -412,25 +457,25 @@ of hops, where information will be exchanged between two nodes.
considered as single public key for simplicity in this explanation.*
* | 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
are encrypted with the RSA public key of the destination and tramsitted over the network.
contains the following data: The newly generated X25519 public key *LKi*.
* | The broadcasted packet will be directed through the network according to the rules laid out
previously.
* | 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
request packet. If the path is not *proven* within some set amount of time, the entry will be
dropped from the *link table* again.
request packet. If the link request packet is not *proven* by the addressed destination within some
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
accept the request. If it is accepted, the destination will also generate a new X25519 private/public
key pair, and perform a Diffie Hellman Key Exchange, deriving a new symmetric key that will be used
to encrypt the channel, once it has been established.
* | When the destination receives the link request packet, it will decide whether to accept the request.
If it is accepted, the destination will also generate a new X25519 private/public key pair, and
perform a Diffie Hellman Key Exchange, deriving a new symmetric key that will be used to encrypt the
channel, once it has been established.
* | 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
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*
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.
.. _understanding-packetformat:
Binary Packet Format
--------------------
@@ -651,8 +698,8 @@ Binary Packet Format
wire size including all fields.
- Path Request : 33 bytes
- Announce : 323 bytes
- Link Request : 141 bytes
- Link Proof : 205 bytes
- Announce : 151 bytes
- Link Request : 77 bytes
- Link Proof : 77 bytes
- Link RTT packet : 86 bytes
- Link keepalive : 14 bytes
+12 -4
View File
@@ -25,11 +25,11 @@ What does Reticulum Offer?
* 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
@@ -37,6 +37,8 @@ What does Reticulum Offer?
* IVs are generated through os.urandom()
* Keys are ephemeral and derived from an ECDH key exchange on Curve25519
* Unforgeable packet delivery confirmations
* A variety of supported interface types
@@ -51,10 +53,16 @@ What does Reticulum Offer?
* 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?
============================
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,
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
+1 -1
View File
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
setuptools.setup(
name="rns",
version="0.2.0",
version="0.2.1",
author="Mark Qvist",
author_email="mark@unsigned.io",
description="Self-configuring, encrypted and resilient mesh networking stack for LoRa, packet radio, WiFi and everything in between",