mirror of
https://github.com/markqvist/Reticulum.git
synced 2024-11-22 13:40:19 +00:00
Compare commits
13 Commits
7f2154110c
...
6ac393bbcd
Author | SHA1 | Date | |
---|---|---|---|
|
6ac393bbcd | ||
|
0c04663942 | ||
|
bfa216de54 | ||
|
a4b1606921 | ||
|
ad0db9c95c | ||
|
2fdcbec860 | ||
|
dd889d16d4 | ||
|
a11f14e75f | ||
|
c32086c6f1 | ||
|
54eaff203f | ||
|
2bf75f60bc | ||
|
3f64141455 | ||
|
063ea2bb7a |
@ -5,7 +5,6 @@
|
|||||||
# of the packet. #
|
# of the packet. #
|
||||||
##########################################################
|
##########################################################
|
||||||
|
|
||||||
import os
|
|
||||||
import argparse
|
import argparse
|
||||||
import RNS
|
import RNS
|
||||||
|
|
||||||
@ -28,19 +27,8 @@ def server(configpath):
|
|||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Load identity from file if it exist or randomley create
|
# Randomly create a new identity for our echo server
|
||||||
if configpath:
|
server_identity = RNS.Identity()
|
||||||
ifilepath = "%s/storage/identitiesy/%s" % (configpath,APP_NAME)
|
|
||||||
else:
|
|
||||||
ifilepath = "%s/storage/identities/%s" % (RNS.Reticulum.configdir,APP_NAME)
|
|
||||||
if os.path.exists(ifilepath):
|
|
||||||
# Load identity from file
|
|
||||||
server_identity = RNS.Identity.from_file(ifilepath)
|
|
||||||
RNS.log("loaded identity from file: "+ifilepath, RNS.LOG_VERBOSE)
|
|
||||||
else:
|
|
||||||
# Randomly create a new identity for our echo example
|
|
||||||
server_identity = RNS.Identity()
|
|
||||||
RNS.log("created new identity", RNS.LOG_VERBOSE)
|
|
||||||
|
|
||||||
# We create a destination that clients can query. We want
|
# We create a destination that clients can query. We want
|
||||||
# to be able to verify echo replies to our clients, so we
|
# to be able to verify echo replies to our clients, so we
|
||||||
@ -340,4 +328,4 @@ if __name__ == "__main__":
|
|||||||
client(args.destination, configarg, timeout=timeoutarg)
|
client(args.destination, configarg, timeout=timeoutarg)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("")
|
print("")
|
||||||
exit()
|
exit()
|
@ -28,8 +28,8 @@ def server(configpath):
|
|||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
|
# Randomly create a new identity for our link example
|
||||||
server_identity = RNS.Identity()
|
server_identity = RNS.Identity()
|
||||||
RNS.log("created new identity", RNS.LOG_VERBOSE)
|
|
||||||
|
|
||||||
# We create a destination that clients can connect to. We
|
# We create a destination that clients can connect to. We
|
||||||
# want clients to create links to this destination, so we
|
# want clients to create links to this destination, so we
|
||||||
@ -58,7 +58,7 @@ def server_loop(destination):
|
|||||||
" running, waiting for a connection."
|
" running, waiting for a connection."
|
||||||
)
|
)
|
||||||
|
|
||||||
RNS.log("Hit enter to manually send an announce (Ctrl-C or \"quit\" to quit)")
|
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
|
||||||
|
|
||||||
# We enter a loop that runs until the users exits.
|
# We enter a loop that runs until the users exits.
|
||||||
# If the user hits enter, we will announce our server
|
# If the user hits enter, we will announce our server
|
||||||
@ -68,12 +68,6 @@ def server_loop(destination):
|
|||||||
entered = input()
|
entered = input()
|
||||||
destination.announce()
|
destination.announce()
|
||||||
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
|
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
|
||||||
if entered == "quit":
|
|
||||||
if latest_client_link:
|
|
||||||
latest_client_link.teardown()
|
|
||||||
break
|
|
||||||
print("")
|
|
||||||
exit()
|
|
||||||
|
|
||||||
# When a client establishes a link to our server
|
# When a client establishes a link to our server
|
||||||
# destination, this function will be called with
|
# destination, this function will be called with
|
||||||
@ -294,4 +288,4 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("")
|
print("")
|
||||||
exit()
|
exit()
|
343
Examples/Ratchets.py
Normal file
343
Examples/Ratchets.py
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
##########################################################
|
||||||
|
# This RNS example demonstrates a simple client/server #
|
||||||
|
# echo utility that uses ratchets to rotate encryption #
|
||||||
|
# keys everytime an announce is sent. #
|
||||||
|
##########################################################
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import RNS
|
||||||
|
|
||||||
|
# Let's define an app name. We'll use this for all
|
||||||
|
# destinations we create. Since this echo example
|
||||||
|
# is part of a range of example utilities, we'll put
|
||||||
|
# them all within the app namespace "example_utilities"
|
||||||
|
APP_NAME = "example_utilities"
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################
|
||||||
|
#### Server Part #########################################
|
||||||
|
##########################################################
|
||||||
|
|
||||||
|
# This initialisation is executed when the users chooses
|
||||||
|
# to run as a server
|
||||||
|
def server(configpath):
|
||||||
|
global reticulum
|
||||||
|
|
||||||
|
# We must first initialise Reticulum
|
||||||
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
|
# TODO: Remove
|
||||||
|
RNS.loglevel = RNS.LOG_DEBUG
|
||||||
|
|
||||||
|
# Randomly create a new identity for our echo server
|
||||||
|
server_identity = RNS.Identity()
|
||||||
|
|
||||||
|
# We create a destination that clients can query. We want
|
||||||
|
# to be able to verify echo replies to our clients, so we
|
||||||
|
# create a "single" destination that can receive encrypted
|
||||||
|
# messages. This way the client can send a request and be
|
||||||
|
# certain that no-one else than this destination was able
|
||||||
|
# to read it.
|
||||||
|
echo_destination = RNS.Destination(
|
||||||
|
server_identity,
|
||||||
|
RNS.Destination.IN,
|
||||||
|
RNS.Destination.SINGLE,
|
||||||
|
APP_NAME,
|
||||||
|
"ratchet",
|
||||||
|
"echo",
|
||||||
|
"request"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Enable ratchets on the destination by providing a file
|
||||||
|
# path to store ratchets. In this example, we will just
|
||||||
|
# use a temporary file, but in real-world applications,
|
||||||
|
# it's extremely important to keep this file secure, since
|
||||||
|
# it contains encryption keys for the destination.
|
||||||
|
destination_hexhash = RNS.hexrep(echo_destination.hash, delimit=False)
|
||||||
|
echo_destination.enable_ratchets(f"/tmp/{destination_hexhash}.ratchets")
|
||||||
|
|
||||||
|
# We configure the destination to automatically prove all
|
||||||
|
# packets addressed to it. By doing this, RNS will automatically
|
||||||
|
# generate a proof for each incoming packet and transmit it
|
||||||
|
# back to the sender of that packet.
|
||||||
|
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
|
||||||
|
|
||||||
|
# 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.set_packet_callback(server_callback)
|
||||||
|
|
||||||
|
# Everything's ready!
|
||||||
|
# Let's Wait for client requests or user input
|
||||||
|
announceLoop(echo_destination)
|
||||||
|
|
||||||
|
|
||||||
|
def announceLoop(destination):
|
||||||
|
# Let the user know that everything is ready
|
||||||
|
RNS.log(
|
||||||
|
"Ratcheted echo server "+
|
||||||
|
RNS.prettyhexrep(destination.hash)+
|
||||||
|
" running, hit enter to manually send an announce (Ctrl-C to quit)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# We enter a loop that runs until the users exits.
|
||||||
|
# If the user hits enter, we will announce our server
|
||||||
|
# destination on the network, which will let clients
|
||||||
|
# know how to create messages directed towards it.
|
||||||
|
while True:
|
||||||
|
entered = input()
|
||||||
|
destination.announce()
|
||||||
|
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
|
||||||
|
|
||||||
|
|
||||||
|
def server_callback(message, packet):
|
||||||
|
global reticulum
|
||||||
|
|
||||||
|
# Tell the user that we received an echo request, and
|
||||||
|
# that we are going to send a reply to the requester.
|
||||||
|
# Sending the proof is handled automatically, since we
|
||||||
|
# set up the destination to prove all incoming packets.
|
||||||
|
|
||||||
|
reception_stats = ""
|
||||||
|
if reticulum.is_connected_to_shared_instance:
|
||||||
|
reception_rssi = reticulum.get_packet_rssi(packet.packet_hash)
|
||||||
|
reception_snr = reticulum.get_packet_snr(packet.packet_hash)
|
||||||
|
|
||||||
|
if reception_rssi != None:
|
||||||
|
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
|
||||||
|
|
||||||
|
if reception_snr != None:
|
||||||
|
reception_stats += " [SNR "+str(reception_snr)+" dBm]"
|
||||||
|
|
||||||
|
else:
|
||||||
|
if packet.rssi != None:
|
||||||
|
reception_stats += " [RSSI "+str(packet.rssi)+" dBm]"
|
||||||
|
|
||||||
|
if packet.snr != None:
|
||||||
|
reception_stats += " [SNR "+str(packet.snr)+" dB]"
|
||||||
|
|
||||||
|
RNS.log("Received packet from echo client, proof sent"+reception_stats)
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################
|
||||||
|
#### Client Part #########################################
|
||||||
|
##########################################################
|
||||||
|
|
||||||
|
# This initialisation is executed when the users chooses
|
||||||
|
# to run as a client
|
||||||
|
def client(destination_hexhash, configpath, timeout=None):
|
||||||
|
global reticulum
|
||||||
|
|
||||||
|
# We need a binary representation of the destination
|
||||||
|
# hash that was entered on the command line
|
||||||
|
try:
|
||||||
|
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||||
|
if len(destination_hexhash) != dest_len:
|
||||||
|
raise ValueError(
|
||||||
|
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)
|
||||||
|
)
|
||||||
|
|
||||||
|
destination_hash = bytes.fromhex(destination_hexhash)
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("Invalid destination entered. Check your input!")
|
||||||
|
RNS.log(str(e)+"\n")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
# We must first initialise Reticulum
|
||||||
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
|
# We override the loglevel to provide feedback when
|
||||||
|
# an announce is received
|
||||||
|
if RNS.loglevel < RNS.LOG_INFO:
|
||||||
|
RNS.loglevel = RNS.LOG_INFO
|
||||||
|
|
||||||
|
# Tell the user that the client is ready!
|
||||||
|
RNS.log(
|
||||||
|
"Echo client ready, hit enter to send echo request to "+
|
||||||
|
destination_hexhash+
|
||||||
|
" (Ctrl-C to quit)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# We enter a loop that runs until the user exits.
|
||||||
|
# If the user hits enter, we will try to send an
|
||||||
|
# echo request to the destination specified on the
|
||||||
|
# command line.
|
||||||
|
while True:
|
||||||
|
input()
|
||||||
|
|
||||||
|
# Let's first check if RNS knows a path to the destination.
|
||||||
|
# If it does, we'll load the server identity and create a packet
|
||||||
|
if RNS.Transport.has_path(destination_hash):
|
||||||
|
|
||||||
|
# To address the server, we need to know it's public
|
||||||
|
# key, so we check if Reticulum knows this destination.
|
||||||
|
# This is done by calling the "recall" method of the
|
||||||
|
# Identity module. If the destination is known, it will
|
||||||
|
# return an Identity instance that can be used in
|
||||||
|
# outgoing destinations.
|
||||||
|
server_identity = RNS.Identity.recall(destination_hash)
|
||||||
|
|
||||||
|
# We got the correct identity instance from the
|
||||||
|
# recall method, so let's create an outgoing
|
||||||
|
# destination. We use the naming convention:
|
||||||
|
# example_utilities.ratchet.echo.request
|
||||||
|
# This matches the naming we specified in the
|
||||||
|
# server part of the code.
|
||||||
|
request_destination = RNS.Destination(
|
||||||
|
server_identity,
|
||||||
|
RNS.Destination.OUT,
|
||||||
|
RNS.Destination.SINGLE,
|
||||||
|
APP_NAME,
|
||||||
|
"ratchet",
|
||||||
|
"echo",
|
||||||
|
"request"
|
||||||
|
)
|
||||||
|
|
||||||
|
# The destination is ready, so let's create a packet.
|
||||||
|
# We set the destination to the request_destination
|
||||||
|
# that was just created, and the only data we add
|
||||||
|
# is a random hash.
|
||||||
|
echo_request = RNS.Packet(request_destination, RNS.Identity.get_random_hash())
|
||||||
|
|
||||||
|
# Send the packet! If the packet is successfully
|
||||||
|
# sent, it will return a PacketReceipt instance.
|
||||||
|
packet_receipt = echo_request.send()
|
||||||
|
|
||||||
|
# If the user specified a timeout, we set this
|
||||||
|
# timeout on the packet receipt, and configure
|
||||||
|
# a callback function, that will get called if
|
||||||
|
# the packet times out.
|
||||||
|
if timeout != None:
|
||||||
|
packet_receipt.set_timeout(timeout)
|
||||||
|
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.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))
|
||||||
|
else:
|
||||||
|
# If we do not know this destination, tell the
|
||||||
|
# user to wait for an announce to arrive.
|
||||||
|
RNS.log("Destination is not yet known. Requesting path...")
|
||||||
|
RNS.log("Hit enter to manually retry once an announce is received.")
|
||||||
|
RNS.Transport.request_path(destination_hash)
|
||||||
|
|
||||||
|
# This function is called when our reply destination
|
||||||
|
# receives a proof packet.
|
||||||
|
def packet_delivered(receipt):
|
||||||
|
global reticulum
|
||||||
|
|
||||||
|
if receipt.status == RNS.PacketReceipt.DELIVERED:
|
||||||
|
rtt = receipt.get_rtt()
|
||||||
|
if (rtt >= 1):
|
||||||
|
rtt = round(rtt, 3)
|
||||||
|
rttstring = str(rtt)+" seconds"
|
||||||
|
else:
|
||||||
|
rtt = round(rtt*1000, 3)
|
||||||
|
rttstring = str(rtt)+" milliseconds"
|
||||||
|
|
||||||
|
reception_stats = ""
|
||||||
|
if reticulum.is_connected_to_shared_instance:
|
||||||
|
reception_rssi = reticulum.get_packet_rssi(receipt.proof_packet.packet_hash)
|
||||||
|
reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash)
|
||||||
|
|
||||||
|
if reception_rssi != None:
|
||||||
|
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
|
||||||
|
|
||||||
|
if reception_snr != None:
|
||||||
|
reception_stats += " [SNR "+str(reception_snr)+" dB]"
|
||||||
|
|
||||||
|
else:
|
||||||
|
if receipt.proof_packet != None:
|
||||||
|
if receipt.proof_packet.rssi != None:
|
||||||
|
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]"
|
||||||
|
|
||||||
|
if receipt.proof_packet.snr != None:
|
||||||
|
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]"
|
||||||
|
|
||||||
|
RNS.log(
|
||||||
|
"Valid reply received from "+
|
||||||
|
RNS.prettyhexrep(receipt.destination.hash)+
|
||||||
|
", round-trip time is "+rttstring+
|
||||||
|
reception_stats
|
||||||
|
)
|
||||||
|
|
||||||
|
# This function is called if a packet times out.
|
||||||
|
def packet_timed_out(receipt):
|
||||||
|
if receipt.status == RNS.PacketReceipt.FAILED:
|
||||||
|
RNS.log("Packet "+RNS.prettyhexrep(receipt.hash)+" timed out")
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################
|
||||||
|
#### Program Startup #####################################
|
||||||
|
##########################################################
|
||||||
|
|
||||||
|
# This part of the program gets run at startup,
|
||||||
|
# and parses input from the user, and then starts
|
||||||
|
# the desired program mode.
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
parser = argparse.ArgumentParser(description="Simple ratcheted echo server and client utility")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-s",
|
||||||
|
"--server",
|
||||||
|
action="store_true",
|
||||||
|
help="wait for incoming packets from clients"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-t",
|
||||||
|
"--timeout",
|
||||||
|
action="store",
|
||||||
|
metavar="s",
|
||||||
|
default=None,
|
||||||
|
help="set a reply timeout in seconds",
|
||||||
|
type=float
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument("--config",
|
||||||
|
action="store",
|
||||||
|
default=None,
|
||||||
|
help="path to alternative Reticulum config directory",
|
||||||
|
type=str
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"destination",
|
||||||
|
nargs="?",
|
||||||
|
default=None,
|
||||||
|
help="hexadecimal hash of the server destination",
|
||||||
|
type=str
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.server:
|
||||||
|
configarg=None
|
||||||
|
if args.config:
|
||||||
|
configarg = args.config
|
||||||
|
server(configarg)
|
||||||
|
else:
|
||||||
|
if args.config:
|
||||||
|
configarg = args.config
|
||||||
|
else:
|
||||||
|
configarg = None
|
||||||
|
|
||||||
|
if args.timeout:
|
||||||
|
timeoutarg = float(args.timeout)
|
||||||
|
else:
|
||||||
|
timeoutarg = None
|
||||||
|
|
||||||
|
if (args.destination == None):
|
||||||
|
print("")
|
||||||
|
parser.print_help()
|
||||||
|
print("")
|
||||||
|
else:
|
||||||
|
client(args.destination, configarg, timeout=timeoutarg)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("")
|
||||||
|
exit()
|
@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors
|
# Copyright (c) 2016-2024 Mark Qvist / unsigned.io and contributors
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -20,11 +20,13 @@
|
|||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
# SOFTWARE.
|
# SOFTWARE.
|
||||||
|
|
||||||
|
import os
|
||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
import RNS
|
import RNS
|
||||||
|
|
||||||
from RNS.Cryptography import Fernet
|
from RNS.Cryptography import Fernet
|
||||||
|
from .vendor import umsgpack as umsgpack
|
||||||
|
|
||||||
class Callbacks:
|
class Callbacks:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -38,14 +40,14 @@ class Destination:
|
|||||||
instances are used both to create outgoing and incoming endpoints. The
|
instances are used both to create outgoing and incoming endpoints. The
|
||||||
destination type will decide if encryption, and what type, is used in
|
destination type will decide if encryption, and what type, is used in
|
||||||
communication with the endpoint. A destination can also announce its
|
communication with the endpoint. A destination can also announce its
|
||||||
presence on the network, which will also distribute necessary keys for
|
presence on the network, which will distribute necessary keys for
|
||||||
encrypted communication with it.
|
encrypted communication with it.
|
||||||
|
|
||||||
:param identity: An instance of :ref:`RNS.Identity<api-identity>`. Can hold only public keys for an outgoing destination, or holding private keys for an ingoing.
|
:param identity: An instance of :ref:`RNS.Identity<api-identity>`. Can hold only public keys for an outgoing destination, or holding private keys for an ingoing.
|
||||||
:param direction: ``RNS.Destination.IN`` or ``RNS.Destination.OUT``.
|
:param direction: ``RNS.Destination.IN`` or ``RNS.Destination.OUT``.
|
||||||
:param type: ``RNS.Destination.SINGLE``, ``RNS.Destination.GROUP`` or ``RNS.Destination.PLAIN``.
|
:param type: ``RNS.Destination.SINGLE``, ``RNS.Destination.GROUP`` or ``RNS.Destination.PLAIN``.
|
||||||
:param app_name: A string specifying the app name.
|
:param app_name: A string specifying the app name.
|
||||||
:param \*aspects: Any non-zero number of string arguments.
|
:param \\*aspects: Any non-zero number of string arguments.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
@ -70,6 +72,7 @@ class Destination:
|
|||||||
directions = [IN, OUT]
|
directions = [IN, OUT]
|
||||||
|
|
||||||
PR_TAG_WINDOW = 30
|
PR_TAG_WINDOW = 30
|
||||||
|
RATCHET_COUNT = 512
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def expand_name(identity, app_name, *aspects):
|
def expand_name(identity, app_name, *aspects):
|
||||||
@ -137,6 +140,8 @@ class Destination:
|
|||||||
self.type = type
|
self.type = type
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
self.proof_strategy = Destination.PROVE_NONE
|
self.proof_strategy = Destination.PROVE_NONE
|
||||||
|
self.ratchets = None
|
||||||
|
self.ratchets_path = None
|
||||||
self.mtu = 0
|
self.mtu = 0
|
||||||
|
|
||||||
self.path_responses = {}
|
self.path_responses = {}
|
||||||
@ -170,6 +175,57 @@ class Destination:
|
|||||||
"""
|
"""
|
||||||
return "<"+self.name+"/"+self.hexhash+">"
|
return "<"+self.name+"/"+self.hexhash+">"
|
||||||
|
|
||||||
|
def enable_ratchets(self, ratchets_path):
|
||||||
|
if ratchets_path != None:
|
||||||
|
if os.path.isfile(ratchets_path):
|
||||||
|
try:
|
||||||
|
ratchets_file = open(ratchets_path, "rb")
|
||||||
|
persisted_data = umsgpack.unpackb(ratchets_file.read())
|
||||||
|
if "signature" in persisted_data and "ratchets" in persisted_data:
|
||||||
|
if self.identity.validate(persisted_data["signature"], persisted_data["ratchets"]):
|
||||||
|
self.ratchets = umsgpack.unpackb(persisted_data["ratchets"])
|
||||||
|
self.ratchets_path = ratchets_path
|
||||||
|
else:
|
||||||
|
raise KeyError("Invalid ratchet file signature")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.ratchets = None
|
||||||
|
self.ratchets_path = None
|
||||||
|
raise OSError("Could not read ratchet file contents for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
else:
|
||||||
|
RNS.log("No existing ratchet data found, initialising new ratchet file for "+str(self), RNS.LOG_DEBUG)
|
||||||
|
self.ratchets = []
|
||||||
|
self.ratchets_path = ratchets_path
|
||||||
|
self.persist_ratchets()
|
||||||
|
|
||||||
|
RNS.log("Ratchets enabled on "+str(self), RNS.LOG_DEBUG) # TODO: Remove
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError("No ratchet file path specified for "+str(self))
|
||||||
|
|
||||||
|
def persist_ratchets(self):
|
||||||
|
try:
|
||||||
|
packed_ratchets = umsgpack.packb(self.ratchets)
|
||||||
|
persisted_data = {"signature": self.sign(packed_ratchets), "ratchets": packed_ratchets}
|
||||||
|
ratchets_file = open(self.ratchets_path, "wb")
|
||||||
|
ratchets_file.write(umsgpack.packb(persisted_data))
|
||||||
|
ratchets_file.close()
|
||||||
|
except Exception as e:
|
||||||
|
self.ratchets = None
|
||||||
|
self.ratchets_path = None
|
||||||
|
raise OSError("Could not write ratchet file contents for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
def rotate_ratchets(self):
|
||||||
|
if self.ratchets != None:
|
||||||
|
RNS.log("Rotating ratchets for "+str(self), RNS.LOG_DEBUG) # TODO: Remove
|
||||||
|
new_ratchet = RNS.Identity._generate_ratchet()
|
||||||
|
self.ratchets.insert(0, new_ratchet)
|
||||||
|
if len (self.ratchets) > Destination.RATCHET_COUNT:
|
||||||
|
self.ratchets = self.ratchets[:Destination.RATCHET_COUNT]
|
||||||
|
self.persist_ratchets()
|
||||||
|
else:
|
||||||
|
raise SystemError("Cannot rotate ratchet on "+str(self)+", ratchets are not enabled")
|
||||||
|
|
||||||
def announce(self, app_data=None, path_response=False, attached_interface=None, tag=None, send=True):
|
def announce(self, app_data=None, path_response=False, attached_interface=None, tag=None, send=True):
|
||||||
"""
|
"""
|
||||||
@ -185,6 +241,7 @@ class Destination:
|
|||||||
if self.direction != Destination.IN:
|
if self.direction != Destination.IN:
|
||||||
raise TypeError("Only IN destination types can be announced")
|
raise TypeError("Only IN destination types can be announced")
|
||||||
|
|
||||||
|
ratchet = b""
|
||||||
now = time.time()
|
now = time.time()
|
||||||
stale_responses = []
|
stale_responses = []
|
||||||
for entry_tag in self.path_responses:
|
for entry_tag in self.path_responses:
|
||||||
@ -211,6 +268,13 @@ class Destination:
|
|||||||
destination_hash = self.hash
|
destination_hash = self.hash
|
||||||
random_hash = RNS.Identity.get_random_hash()[0:5]+int(time.time()).to_bytes(5, "big")
|
random_hash = RNS.Identity.get_random_hash()[0:5]+int(time.time()).to_bytes(5, "big")
|
||||||
|
|
||||||
|
if self.ratchets != None:
|
||||||
|
self.rotate_ratchets()
|
||||||
|
ratchet = RNS.Identity._ratchet_public_bytes(self.ratchets[0])
|
||||||
|
|
||||||
|
# TODO: Remove debug output
|
||||||
|
RNS.log(f"Including ratchet {RNS.prettyhexrep(RNS.Identity.truncated_hash(ratchet))} in announce", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
if app_data == None and self.default_app_data != None:
|
if app_data == None and self.default_app_data != None:
|
||||||
if isinstance(self.default_app_data, bytes):
|
if isinstance(self.default_app_data, bytes):
|
||||||
app_data = self.default_app_data
|
app_data = self.default_app_data
|
||||||
@ -219,13 +283,12 @@ class Destination:
|
|||||||
if isinstance(returned_app_data, bytes):
|
if isinstance(returned_app_data, bytes):
|
||||||
app_data = returned_app_data
|
app_data = returned_app_data
|
||||||
|
|
||||||
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash
|
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash+ratchet
|
||||||
if app_data != None:
|
if app_data != None:
|
||||||
signed_data += app_data
|
signed_data += app_data
|
||||||
|
|
||||||
signature = self.identity.sign(signed_data)
|
signature = self.identity.sign(signed_data)
|
||||||
|
announce_data = self.identity.get_public_key()+self.name_hash+random_hash+ratchet+signature
|
||||||
announce_data = self.identity.get_public_key()+self.name_hash+random_hash+signature
|
|
||||||
|
|
||||||
if app_data != None:
|
if app_data != None:
|
||||||
announce_data += app_data
|
announce_data += app_data
|
||||||
@ -237,8 +300,13 @@ class Destination:
|
|||||||
else:
|
else:
|
||||||
announce_context = RNS.Packet.NONE
|
announce_context = RNS.Packet.NONE
|
||||||
|
|
||||||
announce_packet = RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context = announce_context, attached_interface = attached_interface)
|
if ratchet:
|
||||||
|
context_flag = RNS.Packet.FLAG_SET
|
||||||
|
else:
|
||||||
|
context_flag = RNS.Packet.FLAG_UNSET
|
||||||
|
|
||||||
|
announce_packet = RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context = announce_context,
|
||||||
|
attached_interface = attached_interface, context_flag=context_flag)
|
||||||
if send:
|
if send:
|
||||||
announce_packet.send()
|
announce_packet.send()
|
||||||
else:
|
else:
|
||||||
@ -424,7 +492,7 @@ class Destination:
|
|||||||
return plaintext
|
return plaintext
|
||||||
|
|
||||||
if self.type == Destination.SINGLE and self.identity != None:
|
if self.type == Destination.SINGLE and self.identity != None:
|
||||||
return self.identity.encrypt(plaintext)
|
return self.identity.encrypt(plaintext, ratchet=RNS.Identity.get_ratchet(self.hash))
|
||||||
|
|
||||||
if self.type == Destination.GROUP:
|
if self.type == Destination.GROUP:
|
||||||
if hasattr(self, "prv") and self.prv != None:
|
if hasattr(self, "prv") and self.prv != None:
|
||||||
@ -449,7 +517,7 @@ class Destination:
|
|||||||
return ciphertext
|
return ciphertext
|
||||||
|
|
||||||
if self.type == Destination.SINGLE and self.identity != None:
|
if self.type == Destination.SINGLE and self.identity != None:
|
||||||
return self.identity.decrypt(ciphertext)
|
return self.identity.decrypt(ciphertext, ratchets=self.ratchets)
|
||||||
|
|
||||||
if self.type == Destination.GROUP:
|
if self.type == Destination.GROUP:
|
||||||
if hasattr(self, "prv") and self.prv != None:
|
if hasattr(self, "prv") and self.prv != None:
|
||||||
|
170
RNS/Identity.py
170
RNS/Identity.py
@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
# Copyright (c) 2016-2024 Mark Qvist / unsigned.io and contributors.
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -50,7 +50,10 @@ class Identity:
|
|||||||
KEYSIZE = 256*2
|
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.
|
X25519 key size in bits. A complete key is the concatenation of a 256 bit encryption key, and a 256 bit signing key.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
RATCHETSIZE = 256
|
||||||
|
RATCHET_EXPIRY = 60*60*24*30
|
||||||
|
|
||||||
# Non-configurable constants
|
# Non-configurable constants
|
||||||
FERNET_OVERHEAD = RNS.Cryptography.Fernet.FERNET_OVERHEAD
|
FERNET_OVERHEAD = RNS.Cryptography.Fernet.FERNET_OVERHEAD
|
||||||
@ -67,6 +70,7 @@ class Identity:
|
|||||||
|
|
||||||
# Storage
|
# Storage
|
||||||
known_destinations = {}
|
known_destinations = {}
|
||||||
|
known_ratchets = {}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def remember(packet_hash, destination_hash, public_key, app_data = None):
|
def remember(packet_hash, destination_hash, public_key, app_data = None):
|
||||||
@ -221,20 +225,103 @@ class Identity:
|
|||||||
"""
|
"""
|
||||||
return Identity.truncated_hash(os.urandom(Identity.TRUNCATED_HASHLENGTH//8))
|
return Identity.truncated_hash(os.urandom(Identity.TRUNCATED_HASHLENGTH//8))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _ratchet_public_bytes(ratchet):
|
||||||
|
return X25519PrivateKey.from_private_bytes(ratchet).public_key().public_bytes()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _generate_ratchet():
|
||||||
|
ratchet_prv = X25519PrivateKey.generate()
|
||||||
|
ratchet_pub = ratchet_prv.public_key()
|
||||||
|
return ratchet_prv.private_bytes()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _remember_ratchet(destination_hash, ratchet):
|
||||||
|
RNS.log(f"Remembering ratchet {RNS.prettyhexrep(Identity.truncated_hash(ratchet))} for {RNS.prettyhexrep(destination_hash)}", RNS.LOG_DEBUG) # TODO: Remove
|
||||||
|
try:
|
||||||
|
Identity.known_ratchets[destination_hash] = ratchet
|
||||||
|
hexhash = RNS.hexrep(destination_hash, delimit=False)
|
||||||
|
ratchet_data = {"ratchet": ratchet, "received": time.time()}
|
||||||
|
|
||||||
|
ratchetdir = RNS.Reticulum.storagepath+"/ratchets"
|
||||||
|
|
||||||
|
if not os.path.isdir(ratchetdir):
|
||||||
|
os.makedirs(ratchetdir)
|
||||||
|
|
||||||
|
outpath = f"{ratchetdir}/{hexhash}.out"
|
||||||
|
finalpath = f"{ratchetdir}/{hexhash}"
|
||||||
|
ratchet_file = open(outpath, "wb")
|
||||||
|
ratchet_file.write(umsgpack.packb(ratchet_data))
|
||||||
|
ratchet_file.close()
|
||||||
|
os.rename(outpath, finalpath)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log(f"Could not persist ratchet for {RNS.prettyhexrep(destination_hash)} to storage.", RNS.LOG_ERROR)
|
||||||
|
RNS.log(f"The contained exception was: {e}")
|
||||||
|
RNS.trace_exception(e)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_ratchet(destination_hash):
|
||||||
|
if not destination_hash in Identity.known_ratchets:
|
||||||
|
RNS.log(f"Trying to load ratchet for {RNS.prettyhexrep(destination_hash)} from storage") # TODO: Remove
|
||||||
|
ratchetdir = RNS.Reticulum.storagepath+"/ratchets"
|
||||||
|
hexhash = RNS.hexrep(destination_hash, delimit=False)
|
||||||
|
ratchet_path = f"{ratchetdir}/hexhash"
|
||||||
|
if os.path.isfile(ratchet_path):
|
||||||
|
try:
|
||||||
|
ratchet_file = open(ratchet_path, "rb")
|
||||||
|
ratchet_data = umsgpack.unpackb(ratchets_file.read())
|
||||||
|
if time.time() < ratchet_data["received"]+Identity.RATCHET_EXPIRY and len(ratchet_data["ratchet"]) == Identity.RATCHETSIZE//8:
|
||||||
|
Identity.known_ratchets[destination_hash] = ratchet_data["ratchet"]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log(f"An error occurred while loading ratchet data for {RNS.prettyhexrep(destination_hash)} from storage.", RNS.LOG_ERROR)
|
||||||
|
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if destination_hash in Identity.known_ratchets:
|
||||||
|
return Identity.known_ratchets[destination_hash]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_announce(packet, only_validate_signature=False):
|
def validate_announce(packet, only_validate_signature=False):
|
||||||
try:
|
try:
|
||||||
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
if packet.packet_type == RNS.Packet.ANNOUNCE:
|
||||||
|
keysize = Identity.KEYSIZE//8
|
||||||
|
ratchetsize = Identity.RATCHETSIZE//8
|
||||||
|
name_hash_len = Identity.NAME_HASH_LENGTH//8
|
||||||
|
sig_len = Identity.SIGLENGTH//8
|
||||||
destination_hash = packet.destination_hash
|
destination_hash = packet.destination_hash
|
||||||
public_key = packet.data[:Identity.KEYSIZE//8]
|
|
||||||
name_hash = packet.data[Identity.KEYSIZE//8:Identity.KEYSIZE//8+Identity.NAME_HASH_LENGTH//8]
|
|
||||||
random_hash = packet.data[Identity.KEYSIZE//8+Identity.NAME_HASH_LENGTH//8:Identity.KEYSIZE//8+Identity.NAME_HASH_LENGTH//8+10]
|
|
||||||
signature = packet.data[Identity.KEYSIZE//8+Identity.NAME_HASH_LENGTH//8+10:Identity.KEYSIZE//8+Identity.NAME_HASH_LENGTH//8+10+Identity.SIGLENGTH//8]
|
|
||||||
app_data = b""
|
|
||||||
if len(packet.data) > Identity.KEYSIZE//8+Identity.NAME_HASH_LENGTH//8+10+Identity.SIGLENGTH//8:
|
|
||||||
app_data = packet.data[Identity.KEYSIZE//8+Identity.NAME_HASH_LENGTH//8+10+Identity.SIGLENGTH//8:]
|
|
||||||
|
|
||||||
signed_data = destination_hash+public_key+name_hash+random_hash+app_data
|
# Get public key bytes from announce
|
||||||
|
public_key = packet.data[:keysize]
|
||||||
|
|
||||||
|
# If the packet context flag is set,
|
||||||
|
# this announce contains a new ratchet
|
||||||
|
if packet.context_flag == RNS.Packet.FLAG_SET:
|
||||||
|
name_hash = packet.data[keysize:keysize+name_hash_len ]
|
||||||
|
random_hash = packet.data[keysize+name_hash_len:keysize+name_hash_len+10]
|
||||||
|
ratchet = packet.data[keysize+name_hash_len+10:keysize+name_hash_len+10+ratchetsize]
|
||||||
|
signature = packet.data[keysize+name_hash_len+10+ratchetsize:keysize+name_hash_len+10+ratchetsize+sig_len]
|
||||||
|
app_data = b""
|
||||||
|
if len(packet.data) > keysize+name_hash_len+10+sig_len+ratchetsize:
|
||||||
|
app_data = packet.data[keysize+name_hash_len+10+sig_len+ratchetsize:]
|
||||||
|
|
||||||
|
# If the packet context flag is not set,
|
||||||
|
# this announce does not contain a ratchet
|
||||||
|
else:
|
||||||
|
ratchet = b""
|
||||||
|
name_hash = packet.data[keysize:keysize+name_hash_len]
|
||||||
|
random_hash = packet.data[keysize+name_hash_len:keysize+name_hash_len+10]
|
||||||
|
signature = packet.data[keysize+name_hash_len+10:keysize+name_hash_len+10+sig_len]
|
||||||
|
app_data = b""
|
||||||
|
if len(packet.data) > keysize+name_hash_len+10+sig_len:
|
||||||
|
app_data = packet.data[keysize+name_hash_len+10+sig_len:]
|
||||||
|
|
||||||
|
signed_data = destination_hash+public_key+name_hash+random_hash+ratchet+app_data
|
||||||
|
|
||||||
if not len(packet.data) > Identity.KEYSIZE//8+Identity.NAME_HASH_LENGTH//8+10+Identity.SIGLENGTH//8:
|
if not len(packet.data) > Identity.KEYSIZE//8+Identity.NAME_HASH_LENGTH//8+10+Identity.SIGLENGTH//8:
|
||||||
app_data = None
|
app_data = None
|
||||||
@ -281,6 +368,9 @@ class Identity:
|
|||||||
else:
|
else:
|
||||||
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received on "+str(packet.receiving_interface)+signal_str, RNS.LOG_EXTREME)
|
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received on "+str(packet.receiving_interface)+signal_str, RNS.LOG_EXTREME)
|
||||||
|
|
||||||
|
if ratchet:
|
||||||
|
Identity._remember_ratchet(destination_hash, ratchet)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -469,7 +559,7 @@ class Identity:
|
|||||||
def get_context(self):
|
def get_context(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def encrypt(self, plaintext):
|
def encrypt(self, plaintext, ratchet=None):
|
||||||
"""
|
"""
|
||||||
Encrypts information for the identity.
|
Encrypts information for the identity.
|
||||||
|
|
||||||
@ -481,7 +571,13 @@ class Identity:
|
|||||||
ephemeral_key = X25519PrivateKey.generate()
|
ephemeral_key = X25519PrivateKey.generate()
|
||||||
ephemeral_pub_bytes = ephemeral_key.public_key().public_bytes()
|
ephemeral_pub_bytes = ephemeral_key.public_key().public_bytes()
|
||||||
|
|
||||||
shared_key = ephemeral_key.exchange(self.pub)
|
if ratchet != None:
|
||||||
|
RNS.log(f"Encrypting with ratchet {RNS.prettyhexrep(RNS.Identity.truncated_hash(ratchet))}", RNS.LOG_DEBUG) # TODO: Remove
|
||||||
|
target_public_key = X25519PublicKey.from_public_bytes(ratchet)
|
||||||
|
else:
|
||||||
|
target_public_key = self.pub
|
||||||
|
|
||||||
|
shared_key = ephemeral_key.exchange(target_public_key)
|
||||||
|
|
||||||
derived_key = RNS.Cryptography.hkdf(
|
derived_key = RNS.Cryptography.hkdf(
|
||||||
length=32,
|
length=32,
|
||||||
@ -499,7 +595,7 @@ class Identity:
|
|||||||
raise KeyError("Encryption failed because identity does not hold a public key")
|
raise KeyError("Encryption failed because identity does not hold a public key")
|
||||||
|
|
||||||
|
|
||||||
def decrypt(self, ciphertext_token):
|
def decrypt(self, ciphertext_token, ratchets=None):
|
||||||
"""
|
"""
|
||||||
Decrypts information for the identity.
|
Decrypts information for the identity.
|
||||||
|
|
||||||
@ -513,19 +609,43 @@ class Identity:
|
|||||||
try:
|
try:
|
||||||
peer_pub_bytes = ciphertext_token[:Identity.KEYSIZE//8//2]
|
peer_pub_bytes = ciphertext_token[:Identity.KEYSIZE//8//2]
|
||||||
peer_pub = X25519PublicKey.from_public_bytes(peer_pub_bytes)
|
peer_pub = X25519PublicKey.from_public_bytes(peer_pub_bytes)
|
||||||
|
|
||||||
shared_key = self.prv.exchange(peer_pub)
|
|
||||||
|
|
||||||
derived_key = RNS.Cryptography.hkdf(
|
|
||||||
length=32,
|
|
||||||
derive_from=shared_key,
|
|
||||||
salt=self.get_salt(),
|
|
||||||
context=self.get_context(),
|
|
||||||
)
|
|
||||||
|
|
||||||
fernet = Fernet(derived_key)
|
|
||||||
ciphertext = ciphertext_token[Identity.KEYSIZE//8//2:]
|
ciphertext = ciphertext_token[Identity.KEYSIZE//8//2:]
|
||||||
plaintext = fernet.decrypt(ciphertext)
|
|
||||||
|
if ratchets:
|
||||||
|
for ratchet in ratchets:
|
||||||
|
try:
|
||||||
|
ratchet_prv = X25519PrivateKey.from_private_bytes(ratchet)
|
||||||
|
shared_key = ratchet_prv.exchange(peer_pub)
|
||||||
|
derived_key = RNS.Cryptography.hkdf(
|
||||||
|
length=32,
|
||||||
|
derive_from=shared_key,
|
||||||
|
salt=self.get_salt(),
|
||||||
|
context=self.get_context(),
|
||||||
|
)
|
||||||
|
|
||||||
|
fernet = Fernet(derived_key)
|
||||||
|
plaintext = fernet.decrypt(ciphertext)
|
||||||
|
|
||||||
|
# TODO: Remove
|
||||||
|
RNS.log(f"Decrypted with ratchet {RNS.prettyhexrep(RNS.Identity.truncated_hash(ratchet_prv.public_key().public_bytes()))}", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
# RNS.log("Decryption using this ratchet failed", RNS.LOG_DEBUG) # TODO: Remove
|
||||||
|
|
||||||
|
if plaintext == None:
|
||||||
|
shared_key = self.prv.exchange(peer_pub)
|
||||||
|
derived_key = RNS.Cryptography.hkdf(
|
||||||
|
length=32,
|
||||||
|
derive_from=shared_key,
|
||||||
|
salt=self.get_salt(),
|
||||||
|
context=self.get_context(),
|
||||||
|
)
|
||||||
|
|
||||||
|
fernet = Fernet(derived_key)
|
||||||
|
plaintext = fernet.decrypt(ciphertext)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Decryption by "+RNS.prettyhexrep(self.hash)+" failed: "+str(e), RNS.LOG_DEBUG)
|
RNS.log("Decryption by "+RNS.prettyhexrep(self.hash)+" failed: "+str(e), RNS.LOG_DEBUG)
|
||||||
|
@ -116,40 +116,42 @@ class KISS():
|
|||||||
SX1280 = 0x21
|
SX1280 = 0x21
|
||||||
|
|
||||||
def int_data_cmd_to_index(int_data_cmd):
|
def int_data_cmd_to_index(int_data_cmd):
|
||||||
match int_data_cmd:
|
if int_data_cmd == KISS.CMD_INT0_DATA:
|
||||||
case KISS.CMD_INT0_DATA:
|
return 0
|
||||||
return 0
|
elif int_data_cmd == KISS.CMD_INT1_DATA:
|
||||||
case KISS.CMD_INT1_DATA:
|
return 1
|
||||||
return 1
|
elif int_data_cmd == KISS.CMD_INT2_DATA:
|
||||||
case KISS.CMD_INT2_DATA:
|
return 2
|
||||||
return 2
|
elif int_data_cmd == KISS.CMD_INT3_DATA:
|
||||||
case KISS.CMD_INT3_DATA:
|
return 3
|
||||||
return 3
|
elif int_data_cmd == KISS.CMD_INT4_DATA:
|
||||||
case KISS.CMD_INT4_DATA:
|
return 4
|
||||||
return 4
|
elif int_data_cmd == KISS.CMD_INT5_DATA:
|
||||||
case KISS.CMD_INT5_DATA:
|
return 5
|
||||||
return 5
|
elif int_data_cmd == KISS.CMD_INT6_DATA:
|
||||||
case KISS.CMD_INT6_DATA:
|
return 6
|
||||||
return 6
|
elif int_data_cmd == KISS.CMD_INT7_DATA:
|
||||||
case KISS.CMD_INT7_DATA:
|
return 7
|
||||||
return 7
|
elif int_data_cmd == KISS.CMD_INT8_DATA:
|
||||||
case KISS.CMD_INT8_DATA:
|
return 8
|
||||||
return 8
|
elif int_data_cmd == KISS.CMD_INT9_DATA:
|
||||||
case KISS.CMD_INT9_DATA:
|
return 9
|
||||||
return 9
|
elif int_data_cmd == KISS.CMD_INT10_DATA:
|
||||||
case KISS.CMD_INT10_DATA:
|
return 10
|
||||||
return 10
|
elif int_data_cmd == KISS.CMD_INT11_DATA:
|
||||||
case KISS.CMD_INT11_DATA:
|
return 11
|
||||||
return 11
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
def interface_type_to_str(interface_type):
|
def interface_type_to_str(interface_type):
|
||||||
match interface_type:
|
if interface_type == KISS.SX126X or interface_type == KISS.SX1262:
|
||||||
case KISS.SX126X | KISS.SX1262:
|
|
||||||
return "SX126X"
|
return "SX126X"
|
||||||
case KISS.SX127X | KISS.SX1276 | KISS.SX1278:
|
elif interface_type == KISS.SX127X or interface_type == KISS.SX1276 or interface_type == KISS.SX1278:
|
||||||
return "SX127X"
|
return "SX127X"
|
||||||
case KISS.SX128X | KISS.SX1280:
|
elif interface_type == KISS.SX128X or interface_type == KISS.SX1280:
|
||||||
return "SX128X"
|
return "SX128X"
|
||||||
|
else:
|
||||||
|
return "SX127X"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def escape(data):
|
def escape(data):
|
||||||
@ -915,46 +917,45 @@ class RNodeSubInterface(Interface):
|
|||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
match index:
|
if index == 0:
|
||||||
case 0:
|
sel_cmd = KISS.CMD_SEL_INT0
|
||||||
sel_cmd = KISS.CMD_SEL_INT0
|
data_cmd= KISS.CMD_INT0_DATA
|
||||||
data_cmd= KISS.CMD_INT0_DATA
|
elif index == 1:
|
||||||
case 1:
|
sel_cmd = KISS.CMD_SEL_INT1
|
||||||
sel_cmd = KISS.CMD_SEL_INT1
|
data_cmd= KISS.CMD_INT1_DATA
|
||||||
data_cmd= KISS.CMD_INT1_DATA
|
elif index == 2:
|
||||||
case 2:
|
sel_cmd = KISS.CMD_SEL_INT2
|
||||||
sel_cmd = KISS.CMD_SEL_INT2
|
data_cmd= KISS.CMD_INT2_DATA
|
||||||
data_cmd= KISS.CMD_INT2_DATA
|
elif index == 3:
|
||||||
case 3:
|
sel_cmd = KISS.CMD_SEL_INT3
|
||||||
sel_cmd = KISS.CMD_SEL_INT3
|
data_cmd= KISS.CMD_INT3_DATA
|
||||||
data_cmd= KISS.CMD_INT3_DATA
|
elif index == 4:
|
||||||
case 4:
|
sel_cmd = KISS.CMD_SEL_INT4
|
||||||
sel_cmd = KISS.CMD_SEL_INT4
|
data_cmd= KISS.CMD_INT4_DATA
|
||||||
data_cmd= KISS.CMD_INT4_DATA
|
elif index == 5:
|
||||||
case 5:
|
sel_cmd = KISS.CMD_SEL_INT5
|
||||||
sel_cmd = KISS.CMD_SEL_INT5
|
data_cmd= KISS.CMD_INT5_DATA
|
||||||
data_cmd= KISS.CMD_INT5_DATA
|
elif index == 6:
|
||||||
case 6:
|
sel_cmd = KISS.CMD_SEL_INT6
|
||||||
sel_cmd = KISS.CMD_SEL_INT6
|
data_cmd= KISS.CMD_INT6_DATA
|
||||||
data_cmd= KISS.CMD_INT6_DATA
|
elif index == 7:
|
||||||
case 7:
|
sel_cmd = KISS.CMD_SEL_INT7
|
||||||
sel_cmd = KISS.CMD_SEL_INT7
|
data_cmd= KISS.CMD_INT7_DATA
|
||||||
data_cmd= KISS.CMD_INT7_DATA
|
elif index == 8:
|
||||||
case 8:
|
sel_cmd = KISS.CMD_SEL_INT8
|
||||||
sel_cmd = KISS.CMD_SEL_INT8
|
data_cmd= KISS.CMD_INT8_DATA
|
||||||
data_cmd= KISS.CMD_INT8_DATA
|
elif index == 9:
|
||||||
case 9:
|
sel_cmd = KISS.CMD_SEL_INT9
|
||||||
sel_cmd = KISS.CMD_SEL_INT9
|
data_cmd= KISS.CMD_INT9_DATA
|
||||||
data_cmd= KISS.CMD_INT9_DATA
|
elif index == 10:
|
||||||
case 10:
|
sel_cmd = KISS.CMD_SEL_INT10
|
||||||
sel_cmd = KISS.CMD_SEL_INT10
|
data_cmd= KISS.CMD_INT10_DATA
|
||||||
data_cmd= KISS.CMD_INT10_DATA
|
elif index == 11:
|
||||||
case 11:
|
sel_cmd = KISS.CMD_SEL_INT11
|
||||||
sel_cmd = KISS.CMD_SEL_INT11
|
data_cmd= KISS.CMD_INT11_DATA
|
||||||
data_cmd= KISS.CMD_INT11_DATA
|
else:
|
||||||
case _:
|
sel_cmd = KISS.CMD_SEL_INT0
|
||||||
sel_cmd = KISS.CMD_SEL_INT0
|
data_cmd= KISS.CMD_INT0_DATA
|
||||||
data_cmd= KISS.CMD_INT0_DATA
|
|
||||||
|
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
# Copyright (c) 2016-2024 Mark Qvist / unsigned.io and contributors.
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -142,6 +142,7 @@ class Link:
|
|||||||
raise TypeError("Links can only be established to the \"single\" destination type")
|
raise TypeError("Links can only be established to the \"single\" destination type")
|
||||||
self.rtt = None
|
self.rtt = None
|
||||||
self.establishment_cost = 0
|
self.establishment_cost = 0
|
||||||
|
self.establishment_rate = None
|
||||||
self.callbacks = LinkCallbacks()
|
self.callbacks = LinkCallbacks()
|
||||||
self.resource_strategy = Link.ACCEPT_NONE
|
self.resource_strategy = Link.ACCEPT_NONE
|
||||||
self.outgoing_resources = []
|
self.outgoing_resources = []
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
# Copyright (c) 2016-2024 Mark Qvist / unsigned.io and contributors.
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -83,6 +83,10 @@ class Packet:
|
|||||||
LRRTT = 0xFE # Packet is a link request round-trip time measurement
|
LRRTT = 0xFE # Packet is a link request round-trip time measurement
|
||||||
LRPROOF = 0xFF # Packet is a link request proof
|
LRPROOF = 0xFF # Packet is a link request proof
|
||||||
|
|
||||||
|
# Context flag values
|
||||||
|
FLAG_SET = 0x01
|
||||||
|
FLAG_UNSET = 0x00
|
||||||
|
|
||||||
# This is used to calculate allowable
|
# This is used to calculate allowable
|
||||||
# payload sizes
|
# payload sizes
|
||||||
HEADER_MAXSIZE = RNS.Reticulum.HEADER_MAXSIZE
|
HEADER_MAXSIZE = RNS.Reticulum.HEADER_MAXSIZE
|
||||||
@ -102,7 +106,9 @@ class Packet:
|
|||||||
|
|
||||||
TIMEOUT_PER_HOP = RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
|
TIMEOUT_PER_HOP = RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
|
||||||
|
|
||||||
def __init__(self, destination, data, packet_type = DATA, context = NONE, transport_type = RNS.Transport.BROADCAST, header_type = HEADER_1, transport_id = None, attached_interface = None, create_receipt = True):
|
def __init__(self, destination, data, packet_type = DATA, context = NONE, transport_type = RNS.Transport.BROADCAST,
|
||||||
|
header_type = HEADER_1, transport_id = None, attached_interface = None, create_receipt = True, context_flag=FLAG_UNSET):
|
||||||
|
|
||||||
if destination != None:
|
if destination != None:
|
||||||
if transport_type == None:
|
if transport_type == None:
|
||||||
transport_type = RNS.Transport.BROADCAST
|
transport_type = RNS.Transport.BROADCAST
|
||||||
@ -111,6 +117,7 @@ class Packet:
|
|||||||
self.packet_type = packet_type
|
self.packet_type = packet_type
|
||||||
self.transport_type = transport_type
|
self.transport_type = transport_type
|
||||||
self.context = context
|
self.context = context
|
||||||
|
self.context_flag = context_flag
|
||||||
|
|
||||||
self.hops = 0;
|
self.hops = 0;
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
@ -142,9 +149,10 @@ class Packet:
|
|||||||
|
|
||||||
def get_packed_flags(self):
|
def get_packed_flags(self):
|
||||||
if self.context == Packet.LRPROOF:
|
if self.context == Packet.LRPROOF:
|
||||||
packed_flags = (self.header_type << 6) | (self.transport_type << 4) | (RNS.Destination.LINK << 2) | self.packet_type
|
packed_flags = (self.header_type << 6) | (self.context_flag << 5) | (self.transport_type << 4) | (RNS.Destination.LINK << 2) | self.packet_type
|
||||||
else:
|
else:
|
||||||
packed_flags = (self.header_type << 6) | (self.transport_type << 4) | (self.destination.type << 2) | self.packet_type
|
packed_flags = (self.header_type << 6) | (self.context_flag << 5) | (self.transport_type << 4) | (self.destination.type << 2) | self.packet_type
|
||||||
|
|
||||||
return packed_flags
|
return packed_flags
|
||||||
|
|
||||||
def pack(self):
|
def pack(self):
|
||||||
@ -216,7 +224,8 @@ class Packet:
|
|||||||
self.hops = self.raw[1]
|
self.hops = self.raw[1]
|
||||||
|
|
||||||
self.header_type = (self.flags & 0b01000000) >> 6
|
self.header_type = (self.flags & 0b01000000) >> 6
|
||||||
self.transport_type = (self.flags & 0b00110000) >> 4
|
self.context_flag = (self.flags & 0b00100000) >> 5
|
||||||
|
self.transport_type = (self.flags & 0b00010000) >> 4
|
||||||
self.destination_type = (self.flags & 0b00001100) >> 2
|
self.destination_type = (self.flags & 0b00001100) >> 2
|
||||||
self.packet_type = (self.flags & 0b00000011)
|
self.packet_type = (self.flags & 0b00000011)
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
# Copyright (c) 2016-2024 Mark Qvist / unsigned.io and contributors.
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# MIT License
|
# MIT License
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2023 Mark Qvist / unsigned.io and contributors.
|
# Copyright (c) 2016-2024 Mark Qvist / unsigned.io and contributors.
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -403,7 +403,8 @@ class Transport:
|
|||||||
header_type = RNS.Packet.HEADER_2,
|
header_type = RNS.Packet.HEADER_2,
|
||||||
transport_type = Transport.TRANSPORT,
|
transport_type = Transport.TRANSPORT,
|
||||||
transport_id = Transport.identity.hash,
|
transport_id = Transport.identity.hash,
|
||||||
attached_interface = attached_interface
|
attached_interface = attached_interface,
|
||||||
|
context_flag = packet.context_flag,
|
||||||
)
|
)
|
||||||
|
|
||||||
new_packet.hops = announce_entry[4]
|
new_packet.hops = announce_entry[4]
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = "0.7.6"
|
__version__ = "0.7.7"
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -693,7 +693,8 @@ Wire Format
|
|||||||
[HEADER 2 bytes] [ADDRESSES 16/32 bytes] [CONTEXT 1 byte] [DATA 0-465 bytes]
|
[HEADER 2 bytes] [ADDRESSES 16/32 bytes] [CONTEXT 1 byte] [DATA 0-465 bytes]
|
||||||
|
|
||||||
* The HEADER field is 2 bytes long.
|
* The HEADER field is 2 bytes long.
|
||||||
* Byte 1: [IFAC Flag], [Header Type], [Propagation Type], [Destination Type] and [Packet Type]
|
* Byte 1: [IFAC Flag], [Header Type], [Context Flag], [Propagation Type],
|
||||||
|
[Destination Type] and [Packet Type]
|
||||||
* Byte 2: Number of hops
|
* Byte 2: Number of hops
|
||||||
|
|
||||||
* Interface Access Code field if the IFAC flag was set.
|
* Interface Access Code field if the IFAC flag was set.
|
||||||
@ -725,12 +726,16 @@ Wire Format
|
|||||||
type 2 1 Two byte header, two 16 byte address fields
|
type 2 1 Two byte header, two 16 byte address fields
|
||||||
|
|
||||||
|
|
||||||
|
Context Flag
|
||||||
|
-----------------
|
||||||
|
unset 0 The context flag is used for various types
|
||||||
|
set 1 of signalling, depending on packet context
|
||||||
|
|
||||||
|
|
||||||
Propagation Types
|
Propagation Types
|
||||||
-----------------
|
-----------------
|
||||||
broadcast 00
|
broadcast 0
|
||||||
transport 01
|
transport 1
|
||||||
reserved 10
|
|
||||||
reserved 11
|
|
||||||
|
|
||||||
|
|
||||||
Destination Types
|
Destination Types
|
||||||
|
@ -656,7 +656,6 @@ the Packet interface.</p>
|
|||||||
<span class="c1"># of the packet. #</span>
|
<span class="c1"># of the packet. #</span>
|
||||||
<span class="c1">##########################################################</span>
|
<span class="c1">##########################################################</span>
|
||||||
|
|
||||||
<span class="kn">import</span> <span class="nn">os</span>
|
|
||||||
<span class="kn">import</span> <span class="nn">argparse</span>
|
<span class="kn">import</span> <span class="nn">argparse</span>
|
||||||
<span class="kn">import</span> <span class="nn">RNS</span>
|
<span class="kn">import</span> <span class="nn">RNS</span>
|
||||||
|
|
||||||
@ -679,19 +678,8 @@ the Packet interface.</p>
|
|||||||
<span class="c1"># We must first initialise Reticulum</span>
|
<span class="c1"># We must first initialise Reticulum</span>
|
||||||
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
|
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
|
||||||
|
|
||||||
<span class="c1"># Load identity from file if it exist or randomley create</span>
|
<span class="c1"># Randomly create a new identity for our echo server</span>
|
||||||
<span class="k">if</span> <span class="n">configpath</span><span class="p">:</span>
|
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
|
||||||
<span class="n">ifilepath</span> <span class="o">=</span> <span class="s2">"</span><span class="si">%s</span><span class="s2">/storage/identitiesy/</span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">configpath</span><span class="p">,</span><span class="n">APP_NAME</span><span class="p">)</span>
|
|
||||||
<span class="k">else</span><span class="p">:</span>
|
|
||||||
<span class="n">ifilepath</span> <span class="o">=</span> <span class="s2">"</span><span class="si">%s</span><span class="s2">/storage/identities/</span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">configdir</span><span class="p">,</span><span class="n">APP_NAME</span><span class="p">)</span>
|
|
||||||
<span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">ifilepath</span><span class="p">):</span>
|
|
||||||
<span class="c1"># Load identity from file</span>
|
|
||||||
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="o">.</span><span class="n">from_file</span><span class="p">(</span><span class="n">ifilepath</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">"loaded identity from file: "</span><span class="o">+</span><span class="n">ifilepath</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_VERBOSE</span><span class="p">)</span>
|
|
||||||
<span class="k">else</span><span class="p">:</span>
|
|
||||||
<span class="c1"># Randomly create a new identity for our echo example</span>
|
|
||||||
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
|
|
||||||
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">"created new identity"</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_VERBOSE</span><span class="p">)</span>
|
|
||||||
|
|
||||||
<span class="c1"># We create a destination that clients can query. We want</span>
|
<span class="c1"># We create a destination that clients can query. We want</span>
|
||||||
<span class="c1"># to be able to verify echo replies to our clients, so we</span>
|
<span class="c1"># to be able to verify echo replies to our clients, so we</span>
|
||||||
@ -1030,8 +1018,8 @@ destination, and passing traffic back and forth over the link.</p>
|
|||||||
<span class="c1"># We must first initialise Reticulum</span>
|
<span class="c1"># We must first initialise Reticulum</span>
|
||||||
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
|
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
|
||||||
|
|
||||||
|
<span class="c1"># Randomly create a new identity for our link example</span>
|
||||||
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
|
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
|
||||||
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">"created new identity"</span><span class="p">,</span> <span class="n">RNS</span><span class="o">.</span><span class="n">LOG_VERBOSE</span><span class="p">)</span>
|
|
||||||
|
|
||||||
<span class="c1"># We create a destination that clients can connect to. We</span>
|
<span class="c1"># We create a destination that clients can connect to. We</span>
|
||||||
<span class="c1"># want clients to create links to this destination, so we</span>
|
<span class="c1"># want clients to create links to this destination, so we</span>
|
||||||
@ -1060,7 +1048,7 @@ destination, and passing traffic back and forth over the link.</p>
|
|||||||
<span class="s2">" running, waiting for a connection."</span>
|
<span class="s2">" running, waiting for a connection."</span>
|
||||||
<span class="p">)</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">"Hit enter to manually send an announce (Ctrl-C or </span><span class="se">\"</span><span class="s2">quit</span><span class="se">\"</span><span class="s2"> to quit)"</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">"Hit enter to manually send an announce (Ctrl-C to quit)"</span><span class="p">)</span>
|
||||||
|
|
||||||
<span class="c1"># We enter a loop that runs until the users exits.</span>
|
<span class="c1"># We enter a loop that runs until the users exits.</span>
|
||||||
<span class="c1"># If the user hits enter, we will announce our server</span>
|
<span class="c1"># If the user hits enter, we will announce our server</span>
|
||||||
@ -1070,12 +1058,6 @@ destination, and passing traffic back and forth over the link.</p>
|
|||||||
<span class="n">entered</span> <span class="o">=</span> <span class="nb">input</span><span class="p">()</span>
|
<span class="n">entered</span> <span class="o">=</span> <span class="nb">input</span><span class="p">()</span>
|
||||||
<span class="n">destination</span><span class="o">.</span><span class="n">announce</span><span class="p">()</span>
|
<span class="n">destination</span><span class="o">.</span><span class="n">announce</span><span class="p">()</span>
|
||||||
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">"Sent announce from "</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">destination</span><span class="o">.</span><span class="n">hash</span><span class="p">))</span>
|
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">"Sent announce from "</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">destination</span><span class="o">.</span><span class="n">hash</span><span class="p">))</span>
|
||||||
<span class="k">if</span> <span class="n">entered</span> <span class="o">==</span> <span class="s2">"quit"</span><span class="p">:</span>
|
|
||||||
<span class="k">if</span> <span class="n">latest_client_link</span><span class="p">:</span>
|
|
||||||
<span class="n">latest_client_link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span>
|
|
||||||
<span class="k">break</span>
|
|
||||||
<span class="nb">print</span><span class="p">(</span><span class="s2">""</span><span class="p">)</span>
|
|
||||||
<span class="n">exit</span><span class="p">()</span>
|
|
||||||
|
|
||||||
<span class="c1"># When a client establishes a link to our server</span>
|
<span class="c1"># When a client establishes a link to our server</span>
|
||||||
<span class="c1"># destination, this function will be called with</span>
|
<span class="c1"># destination, this function will be called with</span>
|
||||||
|
@ -532,7 +532,7 @@ communication for the identity. Be very careful with this method.</p>
|
|||||||
|
|
||||||
<dl class="py method">
|
<dl class="py method">
|
||||||
<dt class="sig sig-object py" id="RNS.Identity.encrypt">
|
<dt class="sig sig-object py" id="RNS.Identity.encrypt">
|
||||||
<span class="sig-name descname"><span class="pre">encrypt</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">plaintext</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity.encrypt" title="Permalink to this definition">#</a></dt>
|
<span class="sig-name descname"><span class="pre">encrypt</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">plaintext</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">ratchet</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.Identity.encrypt" title="Permalink to this definition">#</a></dt>
|
||||||
<dd><p>Encrypts information for the identity.</p>
|
<dd><p>Encrypts information for the identity.</p>
|
||||||
<dl class="field-list simple">
|
<dl class="field-list simple">
|
||||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
|
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
|
||||||
@ -549,7 +549,7 @@ communication for the identity. Be very careful with this method.</p>
|
|||||||
|
|
||||||
<dl class="py method">
|
<dl class="py method">
|
||||||
<dt class="sig sig-object py" id="RNS.Identity.decrypt">
|
<dt class="sig sig-object py" id="RNS.Identity.decrypt">
|
||||||
<span class="sig-name descname"><span class="pre">decrypt</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">ciphertext_token</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>, <em class="sig-param"><span class="n"><span class="pre">ratchets</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.Identity.decrypt" title="Permalink to this definition">#</a></dt>
|
||||||
<dd><p>Decrypts information for the identity.</p>
|
<dd><p>Decrypts information for the identity.</p>
|
||||||
<dl class="field-list simple">
|
<dl class="field-list simple">
|
||||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
|
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
|
||||||
@ -611,7 +611,7 @@ communication for the identity. Be very careful with this method.</p>
|
|||||||
instances are used both to create outgoing and incoming endpoints. The
|
instances are used both to create outgoing and incoming endpoints. The
|
||||||
destination type will decide if encryption, and what type, is used in
|
destination type will decide if encryption, and what type, is used in
|
||||||
communication with the endpoint. A destination can also announce its
|
communication with the endpoint. A destination can also announce its
|
||||||
presence on the network, which will also distribute necessary keys for
|
presence on the network, which will distribute necessary keys for
|
||||||
encrypted communication with it.</p>
|
encrypted communication with it.</p>
|
||||||
<dl class="field-list simple">
|
<dl class="field-list simple">
|
||||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
|
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
|
||||||
|
File diff suppressed because one or more lines are too long
@ -927,7 +927,8 @@ A Reticulum packet is composed of the following fields:
|
|||||||
[HEADER 2 bytes] [ADDRESSES 16/32 bytes] [CONTEXT 1 byte] [DATA 0-465 bytes]
|
[HEADER 2 bytes] [ADDRESSES 16/32 bytes] [CONTEXT 1 byte] [DATA 0-465 bytes]
|
||||||
|
|
||||||
* The HEADER field is 2 bytes long.
|
* The HEADER field is 2 bytes long.
|
||||||
* Byte 1: [IFAC Flag], [Header Type], [Propagation Type], [Destination Type] and [Packet Type]
|
* Byte 1: [IFAC Flag], [Header Type], [Context Flag], [Propagation Type],
|
||||||
|
[Destination Type] and [Packet Type]
|
||||||
* Byte 2: Number of hops
|
* Byte 2: Number of hops
|
||||||
|
|
||||||
* Interface Access Code field if the IFAC flag was set.
|
* Interface Access Code field if the IFAC flag was set.
|
||||||
@ -959,12 +960,16 @@ type 1 0 Two byte header, one 16 byte address field
|
|||||||
type 2 1 Two byte header, two 16 byte address fields
|
type 2 1 Two byte header, two 16 byte address fields
|
||||||
|
|
||||||
|
|
||||||
|
Context Flag
|
||||||
|
-----------------
|
||||||
|
unset 0 The context flag is used for various types
|
||||||
|
set 1 of signalling, depending on packet context
|
||||||
|
|
||||||
|
|
||||||
Propagation Types
|
Propagation Types
|
||||||
-----------------
|
-----------------
|
||||||
broadcast 00
|
broadcast 0
|
||||||
transport 01
|
transport 1
|
||||||
reserved 10
|
|
||||||
reserved 11
|
|
||||||
|
|
||||||
|
|
||||||
Destination Types
|
Destination Types
|
||||||
|
@ -693,7 +693,8 @@ Wire Format
|
|||||||
[HEADER 2 bytes] [ADDRESSES 16/32 bytes] [CONTEXT 1 byte] [DATA 0-465 bytes]
|
[HEADER 2 bytes] [ADDRESSES 16/32 bytes] [CONTEXT 1 byte] [DATA 0-465 bytes]
|
||||||
|
|
||||||
* The HEADER field is 2 bytes long.
|
* The HEADER field is 2 bytes long.
|
||||||
* Byte 1: [IFAC Flag], [Header Type], [Propagation Type], [Destination Type] and [Packet Type]
|
* Byte 1: [IFAC Flag], [Header Type], [Context Flag], [Propagation Type],
|
||||||
|
[Destination Type] and [Packet Type]
|
||||||
* Byte 2: Number of hops
|
* Byte 2: Number of hops
|
||||||
|
|
||||||
* Interface Access Code field if the IFAC flag was set.
|
* Interface Access Code field if the IFAC flag was set.
|
||||||
@ -725,12 +726,16 @@ Wire Format
|
|||||||
type 2 1 Two byte header, two 16 byte address fields
|
type 2 1 Two byte header, two 16 byte address fields
|
||||||
|
|
||||||
|
|
||||||
|
Context Flag
|
||||||
|
-----------------
|
||||||
|
unset 0 The context flag is used for various types
|
||||||
|
set 1 of signalling, depending on packet context
|
||||||
|
|
||||||
|
|
||||||
Propagation Types
|
Propagation Types
|
||||||
-----------------
|
-----------------
|
||||||
broadcast 00
|
broadcast 0
|
||||||
transport 01
|
transport 1
|
||||||
reserved 10
|
|
||||||
reserved 11
|
|
||||||
|
|
||||||
|
|
||||||
Destination Types
|
Destination Types
|
||||||
|
Loading…
Reference in New Issue
Block a user