Compare commits

..

11 Commits

Author SHA1 Message Date
Mark Qvist
6fac96ec18 Mask entire header 2023-02-03 00:11:11 +01:00
Mark Qvist
53ceafcebd Improved IFAC mask derivation 2023-02-02 23:59:02 +01:00
Mark Qvist
4df67304d6 Added payload masking to interfaces with IFAC enabled 2023-02-02 20:48:52 +01:00
Mark Qvist
ac07ba1368 Added Identity generation to rnid utility 2023-02-02 19:26:27 +01:00
Mark Qvist
ece064d46e Updated version 2023-02-02 19:05:15 +01:00
Mark Qvist
86ae42a049 Updated docs 2023-02-02 19:04:52 +01:00
Mark Qvist
08e480387b Added signing and validation to rnid 2023-02-02 19:02:05 +01:00
Mark Qvist
f4241ae9c2 Added basic rnid utility 2023-02-02 17:45:59 +01:00
Mark Qvist
b6928b7d83 Merge branch 'master' of github.com:markqvist/Reticulum 2023-02-02 10:40:58 +01:00
Mark Qvist
143f7fa683 Merge branch 'master' of github.com:markqvist/Reticulum 2023-02-02 10:15:41 +01:00
Mark Qvist
159be78f23 Updated docs 2023-02-01 15:44:23 +01:00
14 changed files with 826 additions and 37 deletions

View File

@ -586,12 +586,42 @@ class Transport:
# Calculate packet access code
ifac = interface.ifac_identity.sign(raw)[-interface.ifac_size:]
# Generate mask
mask = RNS.Cryptography.hkdf(
length=len(raw)+interface.ifac_size,
derive_from=ifac,
salt=interface.ifac_key,
context=None,
)
# Set IFAC flag
new_header = bytes([raw[0] | 0x80, raw[1]])
# Assemble new payload with IFAC and send it
# Assemble new payload with IFAC
new_raw = new_header+ifac+raw[2:]
interface.processOutgoing(new_raw)
# Mask payload
i = 0; masked_raw = b""
for byte in new_raw:
if i == 0:
# Mask first header byte, but make sure the
# IFAC flag is still set
masked_raw += bytes([byte ^ mask[i] | 0x80])
elif i == 1 or i > interface.ifac_size+1:
# Mask second header byte and payload
masked_raw += bytes([byte ^ mask[i]])
else:
# Don't mask the IFAC itself
masked_raw += bytes([byte])
i += 1
# Send it
interface.processOutgoing(masked_raw)
# TODO: Remove
# RNS.log("Mask material : "+RNS.hexrep(mask_material), RNS.LOG_DEBUG)
# RNS.log("Before masking : "+RNS.hexrep(new_raw), RNS.LOG_DEBUG)
# RNS.log("After masking : "+RNS.hexrep(masked_raw), RNS.LOG_DEBUG)
else:
interface.processOutgoing(raw)
@ -905,6 +935,26 @@ class Transport:
# Extract IFAC
ifac = raw[2:2+interface.ifac_size]
# Generate mask
mask = RNS.Cryptography.hkdf(
length=len(raw),
derive_from=ifac,
salt=interface.ifac_key,
context=None,
)
# Unmask payload
i = 0; unmasked_raw = b""
for byte in raw:
if i <= 1 or i > interface.ifac_size+1:
# Unmask header bytes and payload
unmasked_raw += bytes([byte ^ mask[i]])
else:
# Don't unmask IFAC itself
unmasked_raw += bytes([byte])
i += 1
raw = unmasked_raw
# Unset IFAC flag
new_header = bytes([raw[0] & 0x7f, raw[1]])

465
RNS/Utilities/rnid.py Normal file
View File

@ -0,0 +1,465 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2023 Mark Qvist / unsigned.io
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import RNS
import argparse
import time
import sys
import os
import base64
from RNS._version import __version__
APP_NAME = "rnid"
SIG_EXT = "rsg"
ENCRYPT_EXT = "rfe"
CHUNK_SIZE = 16*1024*1024
def spin(until=None, msg=None, timeout=None):
i = 0
syms = "⢄⢂⢁⡁⡈⡐⡠"
if timeout != None:
timeout = time.time()+timeout
print(msg+" ", end=" ")
while (timeout == None or time.time()<timeout) and not until():
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
sys.stdout.flush()
i = (i+1)%len(syms)
print("\r"+" "*len(msg)+" \r", end="")
if timeout != None and time.time() > timeout:
return False
else:
return True
def main():
try:
parser = argparse.ArgumentParser(description="Reticulum Identity & Encryption Utility")
# parser.add_argument("file", nargs="?", default=None, help="input file path", type=str)
parser.add_argument("--config", metavar="path", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
parser.add_argument("-i", "--identity", metavar="identity", action="store", default=None, help="hexadecimal Reticulum Destination hash or path to Identity file", type=str)
parser.add_argument("-g", "--generate", metavar="path", action="store", default=None, help="generate a new Identity")
parser.add_argument("-v", "--verbose", action="count", default=0, help="increase verbosity")
parser.add_argument("-q", "--quiet", action="count", default=0, help="decrease verbosity")
parser.add_argument("-e", "--encrypt", metavar="path", action="store", default=None, help="encrypt file")
parser.add_argument("-d", "--decrypt", metavar="path", action="store", default=None, help="decrypt file")
parser.add_argument("-s", "--sign", metavar="path", action="store", default=None, help="sign file")
parser.add_argument("-V", "--validate", metavar="path", action="store", default=None, help="validate signature")
parser.add_argument("-r", "--read", metavar="path", action="store", default=None, help="input file path", type=str)
parser.add_argument("-w", "--write", metavar="path", action="store", default=None, help="output file path", type=str)
parser.add_argument("-f", "--force", action="store_true", default=None, help="write output even if it overwrites existing files")
parser.add_argument("-I", "--stdin", action="store_true", default=False, help=argparse.SUPPRESS) # "read input from STDIN instead of file"
parser.add_argument("-O", "--stdout", action="store_true", default=False, help=argparse.SUPPRESS) # help="write output to STDOUT instead of file",
parser.add_argument("-R", "--request", action="store_true", default=False, help="request unknown Identities from the network")
parser.add_argument("-t", action="store", metavar="seconds", type=float, help="identity request timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
parser.add_argument("-p", "--print-identity", action="store_true", default=False, help="print identity info and exit")
parser.add_argument("-P", "--print-private", action="store_true", default=False, help="allow displaying private keys")
parser.add_argument("-b", "--base64", action="store_true", default=False, help=argparse.SUPPRESS) # help="Use base64-encoded input and output")
parser.add_argument("--version", action="version", version="rncp {version}".format(version=__version__))
args = parser.parse_args()
ops = 0;
for t in [args.encrypt, args.decrypt, args.validate, args.sign]:
if t:
ops += 1
if ops > 1:
RNS.log("This utility currently only supports one of the encrypt, decrypt, sign or verify operations per invocation", RNS.LOG_ERROR)
exit(1)
if not args.read:
if args.encrypt:
args.read = args.encrypt
if args.decrypt:
args.read = args.decrypt
if args.sign:
args.read = args.sign
identity_str = args.identity
if not args.generate and not identity_str:
print("\nNo identity provided, cannot continue\n")
parser.print_help()
print("")
exit(2)
else:
targetloglevel = 4
verbosity = args.verbose
quietness = args.quiet
if verbosity != 0 or quietness != 0:
targetloglevel = targetloglevel+verbosity-quietness
# Start Reticulum
reticulum = RNS.Reticulum(configdir=args.config, loglevel=targetloglevel)
RNS.compact_log_fmt = True
if args.stdout:
RNS.loglevel = -1
if args.generate:
identity = RNS.Identity()
if not args.force and os.path.isfile(args.generate):
RNS.log("Identity file "+str(args.generate)+" already exists. Not overwriting.", RNS.LOG_ERROR)
exit(3)
else:
try:
identity.to_file(args.generate)
RNS.log("New identity written to "+str(args.generate))
exit(0)
except Exception as e:
RNS.log("An error ocurred while saving the generated Identity.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
exit(4)
identity = None
if len(identity_str) == RNS.Reticulum.TRUNCATED_HASHLENGTH//8*2 and not os.path.isfile(identity_str):
# Try recalling Identity from hex-encoded hash
try:
destination_hash = bytes.fromhex(identity_str)
identity = RNS.Identity.recall(destination_hash)
if identity == None:
if not args.request:
RNS.log("Could not recall Identity for "+RNS.prettyhexrep(destination_hash)+".", RNS.LOG_ERROR)
RNS.log("You can query the network for unknown Identities with the -R option.", RNS.LOG_ERROR)
exit(5)
else:
RNS.Transport.request_path(destination_hash)
def spincheck():
return RNS.Identity.recall(destination_hash) != None
spin(spincheck, "Requesting unknown Identity for "+RNS.prettyhexrep(destination_hash), args.t)
if not spincheck():
RNS.log("Identity request timed out", RNS.LOG_ERROR)
exit(6)
else:
RNS.log("Received Identity "+str(identity)+" for destination "+RNS.prettyhexrep(destination_hash)+" from the network")
identity = RNS.Identity.recall(destination_hash)
else:
RNS.log("Recalled Identity "+str(identity)+" for destination "+RNS.prettyhexrep(destination_hash))
except Exception as e:
RNS.log("Invalid hexadecimal hash provided", RNS.LOG_ERROR)
exit(7)
else:
# Try loading Identity from file
if not os.path.isfile(identity_str):
RNS.log("Specified Identity file not found")
exit(8)
else:
try:
identity = RNS.Identity.from_file(identity_str)
RNS.log("Loaded Identity "+str(identity)+" from "+str(identity_str))
except Exception as e:
RNS.log("Could not decode Identity from specified file")
exit(9)
if identity != None:
if args.print_identity:
RNS.log("Public Key : "+RNS.hexrep(identity.pub_bytes, delimit=False))
if identity.prv:
if args.print_private:
RNS.log("Private Key : "+RNS.hexrep(identity.prv_bytes, delimit=False))
else:
RNS.log("Private Key : Hidden")
exit(0)
if args.validate:
if not args.read and args.validate.lower().endswith("."+SIG_EXT):
args.read = str(args.validate).replace("."+SIG_EXT, "")
if not os.path.isfile(args.validate):
RNS.log("Signature file "+str(args.read)+" not found", RNS.LOG_ERROR)
exit(10)
if not os.path.isfile(args.read):
RNS.log("Input file "+str(args.read)+" not found", RNS.LOG_ERROR)
exit(11)
data_input = None
if args.read:
if not os.path.isfile(args.read):
RNS.log("Input file "+str(args.read)+" not found", RNS.LOG_ERROR)
exit(12)
else:
try:
data_input = open(args.read, "rb")
except Exception as e:
RNS.log("Could not open input file for reading", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
exit(13)
# TODO: Actually expand this to a good solution
# probably need to create a wrapper that takes
# into account not closing stdin when done
# elif args.stdin:
# data_input = sys.stdin
data_output = None
if args.encrypt and not args.write and not args.stdout and args.read:
args.write = str(args.read)+"."+ENCRYPT_EXT
if args.decrypt and not args.write and not args.stdout and args.read and args.read.lower().endswith("."+ENCRYPT_EXT):
args.write = str(args.read).replace("."+ENCRYPT_EXT, "")
if args.sign and identity.prv == None:
RNS.log("Specified Identity does not hold a private key. Cannot sign.", RNS.LOG_ERROR)
exit(14)
if args.sign and not args.write and not args.stdout and args.read:
args.write = str(args.read)+"."+SIG_EXT
if args.write:
if not args.force and os.path.isfile(args.write):
RNS.log("Output file "+str(args.write)+" already exists. Not overwriting.", RNS.LOG_ERROR)
exit(15)
else:
try:
data_output = open(args.write, "wb")
except Exception as e:
RNS.log("Could not open output file for writing", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
exit(15)
# TODO: Actually expand this to a good solution
# probably need to create a wrapper that takes
# into account not closing stdout when done
# elif args.stdout:
# data_output = sys.stdout
if args.sign:
if identity.prv == None:
RNS.log("Specified Identity does not hold a private key. Cannot sign.", RNS.LOG_ERROR)
exit(16)
if not data_input:
if not args.stdout:
RNS.log("Signing requested, but no input data specified", RNS.LOG_ERROR)
exit(17)
else:
if not data_output:
if not args.stdout:
RNS.log("Signing requested, but no output specified", RNS.LOG_ERROR)
exit(18)
if not args.stdout:
RNS.log("Signing "+str(args.read))
try:
data_output.write(identity.sign(data_input.read()))
data_output.close()
data_input.close()
if not args.stdout:
if args.read:
RNS.log("File "+str(args.read)+" signed with "+str(identity)+" to "+str(args.write))
exit(0)
except Exception as e:
if not args.stdout:
RNS.log("An error ocurred while encrypting data.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
try:
data_output.close()
except:
pass
try:
data_input.close()
except:
pass
exit(19)
if args.validate:
if not data_input:
if not args.stdout:
RNS.log("Signature verification requested, but no input data specified", RNS.LOG_ERROR)
exit(20)
else:
# if not args.stdout:
# RNS.log("Verifying "+str(args.validate)+" for "+str(args.read))
try:
try:
sig_input = open(args.validate, "rb")
except Exception as e:
RNS.log("An error ocurred while opening "+str(args.validate)+".", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
exit(21)
validated = identity.validate(sig_input.read(), data_input.read())
sig_input.close()
data_input.close()
if not validated:
if not args.stdout:
RNS.log("Signature "+str(args.validate)+" for file "+str(args.read)+" is invalid", RNS.LOG_ERROR)
exit(22)
else:
if not args.stdout:
RNS.log("Signature "+str(args.validate)+" for file "+str(args.read)+" made by Identity "+str(identity)+" is valid")
exit(0)
except Exception as e:
if not args.stdout:
RNS.log("An error ocurred while validating signature.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
try:
data_output.close()
except:
pass
try:
data_input.close()
except:
pass
exit(23)
if args.encrypt:
if not data_input:
if not args.stdout:
RNS.log("Encryption requested, but no input data specified", RNS.LOG_ERROR)
exit(24)
else:
if not data_output:
if not args.stdout:
RNS.log("Encryption requested, but no output specified", RNS.LOG_ERROR)
exit(25)
if not args.stdout:
RNS.log("Encrypting "+str(args.read))
try:
more_data = True
while more_data:
chunk = data_input.read(CHUNK_SIZE)
if chunk:
data_output.write(identity.encrypt(chunk))
else:
more_data = False
data_output.close()
data_input.close()
if not args.stdout:
if args.read:
RNS.log("File "+str(args.read)+" encrypted for "+str(identity)+" to "+str(args.write))
exit(0)
except Exception as e:
if not args.stdout:
RNS.log("An error ocurred while encrypting data.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
try:
data_output.close()
except:
pass
try:
data_input.close()
except:
pass
exit(26)
if args.decrypt:
if identity.prv == None:
RNS.log("Specified Identity does not hold a private key. Cannot decrypt.", RNS.LOG_ERROR)
exit(27)
if not data_input:
if not args.stdout:
RNS.log("Decryption requested, but no input data specified", RNS.LOG_ERROR)
exit(28)
else:
if not data_output:
if not args.stdout:
RNS.log("Decryption requested, but no output specified", RNS.LOG_ERROR)
exit(29)
if not args.stdout:
RNS.log("Decrypting "+str(args.read)+"...")
try:
more_data = True
while more_data:
chunk = data_input.read(CHUNK_SIZE)
if chunk:
plaintext = identity.decrypt(chunk)
if plaintext == None:
if not args.stdout:
RNS.log("Data could not be decrypted with the specified Identity")
exit(30)
else:
data_output.write(plaintext)
else:
more_data = False
data_output.close()
data_input.close()
if not args.stdout:
if args.read:
RNS.log("File "+str(args.read)+" decrypted with "+str(identity)+" to "+str(args.write))
exit(0)
except Exception as e:
if not args.stdout:
RNS.log("An error ocurred while decrypting data.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
try:
data_output.close()
except:
pass
try:
data_input.close()
except:
pass
exit(31)
if True:
pass
elif False:
pass
else:
print("")
parser.print_help()
print("")
except KeyboardInterrupt:
print("")
exit(255)
if __name__ == "__main__":
main()

View File

@ -1 +1 @@
__version__ = "0.4.7"
__version__ = "0.4.8"

View File

@ -22,9 +22,9 @@ For each release cycle of Reticulum, improvements and additions from the five [P
- [ ] Add bluetooth pairing code output to rnodeconf
- [ ] Transit traffic display in rnstatus
- [ ] JSON output mode for rnstatus
- [ ] Add `rnid` utility
- [ ] Add `rnsign` utility
- [ ] Add `rncrypt` utility
- [x] Add `rnid` utility
- [x] Add `rnsign` utility
- [x] Add `rncrypt` utility
- [ ] Create a standalone RNS Daemon app for Android
- Targets for related applications
- [x] Add offline & paper message transport to LXMF

Binary file not shown.

View File

@ -0,0 +1,4 @@
********************************************
An Explanation of Reticulum for Human Beings
********************************************

View File

@ -115,8 +115,8 @@ Creating a Network With Reticulum
=============================================
To create a network, you will need to specify one or more *interfaces* for
Reticulum to use. This is done in the Reticulum configuration file, which by
default is located at ``~/.reticulum/config``. You can edit this file by hand,
or use the interactive ``rnsconfig`` utility.
default is located at ``~/.reticulum/config``. You can get an example
configuration file with all options via ``rnsd --exampleconfig``.
When Reticulum is started for the first time, it will create a default
configuration file, with one active interface. This default interface uses

View File

@ -107,13 +107,13 @@ guide the design of Reticulum:
Introduction & Basic Functionality
==================================
Reticulum is a networking stack suited for high-latency, low-bandwidth links. Reticulum is at its
Reticulum is a networking stack suited for high-latency, low-bandwidth links. Reticulum is at its
core a *message oriented* system. It is suited for both local point-to-point or point-to-multipoint
scenarios where all nodes are within range of each other, as well as scenarios where packets need
to be transported over multiple hops in a complex network to reach the recipient.
Reticulum does away with the idea of addresses and ports known from IP, TCP and UDP. Instead
Reticulum uses the singular concept of *destinations*. Any application using Reticulum as its
Reticulum uses the singular concept of *destinations*. Any application using Reticulum as its
networking stack will need to create one or more destinations to receive data, and know the
destinations it needs to send data to.
@ -220,7 +220,7 @@ packet.
In actual use of *single* destination naming, it is advisable not to use any uniquely identifying
features in aspect naming. Aspect names should be general terms describing what kind of destination
is represented. The uniquely identifying aspect is always achieved by the appending the public key,
is represented. The uniquely identifying aspect is always achieved by appending the public key,
which expands the destination into a uniquely identifiable one. Reticulum does this automatically.
Any destination on a Reticulum network can be addressed and reached simply by knowing its
@ -239,7 +239,7 @@ To recap, the different destination types should be used in the following situat
* **Plain**
When plain-text communication is desirable, for example when broadcasting information, or for local discovery purposes.
To communicate with a *single* destination, you need to know its public key. Any method for
To communicate with a *single* destination, you need to know its public key. Any method for
obtaining the public key is valid, but Reticulum includes a simple mechanism for making other
nodes aware of your destinations public key, called the *announce*. It is also possible to request
an unknown public key from the network, as all transport instances serve as a distributed ledger
@ -287,7 +287,7 @@ In Reticulum, destinations are allowed to move around the network at will. This
protocols such as IP, where an address is always expected to stay within the network segment it was assigned in.
This limitation does not exist in Reticulum, and any destination is *completely portable* over the entire topography
of the network, and *can even be moved to other Reticulum networks* than the one it was created in, and
still become reachable. To update it's reachability, a destination simply needs to send an announce on any
still become reachable. To update its reachability, a destination simply needs to send an announce on any
networks it is part of. After a short while, it will be globally reachable in the network.
Seeing how *single* destinations are always tied to a private/public key pair leads us to the next topic.
@ -368,7 +368,7 @@ If it is a *Transport Node*, it should be given the configuration directive ``en
The Announce Mechanism in Detail
--------------------------------
When an *announce* for a destination is transmitted by from a Reticulum instance, it will be forwarded by
When an *announce* for a destination is transmitted by a Reticulum instance, it will be forwarded by
any transport node receiving it, but according to some specific rules:
@ -385,7 +385,7 @@ any transport node receiving it, but according to some specific rules:
announces is set at 2%, but can be configured on a per-interface basis.
* | If any given interface does not have enough bandwidth available for retransmitting the announce,
the announce will be assigned a priority inversely proportional to it's hop count, and be inserted
the announce will be assigned a priority inversely proportional to its hop count, and be inserted
into a queue managed by the interface.
* | When the interface has bandwidth available for processing an announce, it will prioritise announces
@ -431,7 +431,7 @@ For exchanges of small amounts of information, Reticulum offers the *Packet* API
* | 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.
an ECDH key exchange with the destination's 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
@ -447,8 +447,8 @@ For exchanges of small amounts of information, Reticulum offers the *Packet* API
* | 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
and signing this hash with its 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 destination's known
public signing key.
* | In case the packet is addressed to a *group* destination type, the packet will be encrypted with the
@ -465,7 +465,7 @@ For exchanges of larger amounts of data, or when longer sessions of bidirectiona
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
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.

View File

@ -377,7 +377,7 @@ output.
# Run rnx on the listening system, specifying which identities
# are allowed to execute commands
rncp --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
rnx --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
# From another system, run a command
rnx 7a55144adf826958a9529a3bcf08b149 "cat /proc/cpuinfo"

270
docs/manual/forhumans.html Normal file
View File

@ -0,0 +1,270 @@
<!doctype html>
<html class="no-js" lang="en">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-5.2.2, furo 2022.09.29"/>
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.4.7 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=d81277517bee4d6b0349d71bb2661d4890b5617c" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
<style>
body {
--color-code-background: #f8f8f8;
--color-code-foreground: black;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
--color-background-primary: #202b38;
--color-background-secondary: #161f27;
--color-foreground-primary: #dbdbdb;
--color-foreground-secondary: #a9b1ba;
--color-brand-primary: #41adff;
--color-background-hover: #161f27;
--color-api-name: #ffbe85;
--color-api-pre-name: #efae75;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
--color-background-primary: #202b38;
--color-background-secondary: #161f27;
--color-foreground-primary: #dbdbdb;
--color-foreground-secondary: #a9b1ba;
--color-brand-primary: #41adff;
--color-background-hover: #161f27;
--color-api-name: #ffbe85;
--color-api-pre-name: #efae75;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-half" viewBox="0 0 24 24">
<title>Auto light/dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="12" cy="12" r="9" />
<path d="M13 12h5" />
<path d="M13 15h4" />
<path d="M13 18h1" />
<path d="M13 9h4" />
<path d="M13 6h1" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
<label class="overlay sidebar-overlay" for="__navigation">
<div class="visually-hidden">Hide navigation sidebar</div>
</label>
<label class="overlay toc-overlay" for="__toc">
<div class="visually-hidden">Hide table of contents sidebar</div>
</label>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<div class="visually-hidden">Toggle site navigation sidebar</div>
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.4.7 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand centered" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.4.7 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder=Search name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<ul>
<li class="toctree-l1"><a class="reference internal" href="whatis.html">What is Reticulum?</a></li>
<li class="toctree-l1"><a class="reference internal" href="gettingstartedfast.html">Getting Started Fast</a></li>
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Supported Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
</ul>
<ul>
<li class="toctree-l1"><a class="reference internal" href="reference.html">API Reference</a></li>
</ul>
</div>
</div>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
<article role="main">
<section id="an-explanation-of-reticulum-for-human-beings">
<h1>An Explanation of Reticulum for Human Beings<a class="headerlink" href="#an-explanation-of-reticulum-for-human-beings" title="Permalink to this heading">#</a></h1>
</section>
</article>
</div>
<footer>
<div class="related-pages">
</div>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2022, Mark Qvist
</div>
Generated with <a href="https://www.sphinx-doc.org/">Sphinx</a> and
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
<div class="icons">
</div>
</div>
</div>
</footer>
</div>
<aside class="toc-drawer no-toc">
</aside>
</div>
</div><script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/sphinx_highlight.js"></script>
<script src="_static/scripts/furo.js"></script>
<script src="_static/clipboard.min.js"></script>
<script src="_static/copybutton.js"></script>
</body>
</html>

View File

@ -304,8 +304,8 @@ network status and connectivity.</p>
<h2>Creating a Network With Reticulum<a class="headerlink" href="#creating-a-network-with-reticulum" title="Permalink to this heading">#</a></h2>
<p>To create a network, you will need to specify one or more <em>interfaces</em> for
Reticulum to use. This is done in the Reticulum configuration file, which by
default is located at <code class="docutils literal notranslate"><span class="pre">~/.reticulum/config</span></code>. You can edit this file by hand,
or use the interactive <code class="docutils literal notranslate"><span class="pre">rnsconfig</span></code> utility.</p>
default is located at <code class="docutils literal notranslate"><span class="pre">~/.reticulum/config</span></code>. You can get an example
configuration file with all options via <code class="docutils literal notranslate"><span class="pre">rnsd</span> <span class="pre">--exampleconfig</span></code>.</p>
<p>When Reticulum is started for the first time, it will create a default
configuration file, with one active interface. This default interface uses
your existing Ethernet and WiFi networks (if any), and only allows you to

File diff suppressed because one or more lines are too long

View File

@ -337,12 +337,12 @@ needs to be purchased.</p>
</section>
<section id="introduction-basic-functionality">
<span id="understanding-basicfunctionality"></span><h2>Introduction &amp; Basic Functionality<a class="headerlink" href="#introduction-basic-functionality" title="Permalink to this heading">#</a></h2>
<p>Reticulum is a networking stack suited for high-latency, low-bandwidth links. Reticulum is at its
<p>Reticulum is a networking stack suited for high-latency, low-bandwidth links. Reticulum is at its
core a <em>message oriented</em> system. It is suited for both local point-to-point or point-to-multipoint
scenarios where all nodes are within range of each other, as well as scenarios where packets need
to be transported over multiple hops in a complex network to reach the recipient.</p>
<p>Reticulum does away with the idea of addresses and ports known from IP, TCP and UDP. Instead
Reticulum uses the singular concept of <em>destinations</em>. Any application using Reticulum as its
Reticulum uses the singular concept of <em>destinations</em>. Any application using Reticulum as its
networking stack will need to create one or more destinations to receive data, and know the
destinations it needs to send data to.</p>
<p>All destinations in Reticulum are _represented_ as a 16 byte hash. This hash is derived from truncating a full
@ -442,7 +442,7 @@ addressable, because their public keys will differ.</p></li>
</ul>
<p>In actual use of <em>single</em> destination naming, it is advisable not to use any uniquely identifying
features in aspect naming. Aspect names should be general terms describing what kind of destination
is represented. The uniquely identifying aspect is always achieved by the appending the public key,
is represented. The uniquely identifying aspect is always achieved by appending the public key,
which expands the destination into a uniquely identifiable one. Reticulum does this automatically.</p>
<p>Any destination on a Reticulum network can be addressed and reached simply by knowing its
destination hash (and public key, but if the public key is not known, it can be requested from the
@ -468,7 +468,7 @@ indirectly, but must first be established through a <em>single</em> destination.
</dl>
</li>
</ul>
<p>To communicate with a <em>single</em> destination, you need to know its public key. Any method for
<p>To communicate with a <em>single</em> destination, you need to know its public key. Any method for
obtaining the public key is valid, but Reticulum includes a simple mechanism for making other
nodes aware of your destinations public key, called the <em>announce</em>. It is also possible to request
an unknown public key from the network, as all transport instances serve as a distributed ledger
@ -509,7 +509,7 @@ certain pattern. This will be detailed in the section
protocols such as IP, where an address is always expected to stay within the network segment it was assigned in.
This limitation does not exist in Reticulum, and any destination is <em>completely portable</em> over the entire topography
of the network, and <em>can even be moved to other Reticulum networks</em> than the one it was created in, and
still become reachable. To update its reachability, a destination simply needs to send an announce on any
still become reachable. To update its reachability, a destination simply needs to send an announce on any
networks it is part of. After a short while, it will be globally reachable in the network.</p>
<p>Seeing how <em>single</em> destinations are always tied to a private/public key pair leads us to the next topic.</p>
</section>
@ -565,7 +565,7 @@ is the default setting.</p>
</section>
<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 heading">#</a></h3>
<p>When an <em>announce</em> for a destination is transmitted by from a Reticulum instance, it will be forwarded by
<p>When an <em>announce</em> for a destination is transmitted by a Reticulum instance, it will be forwarded by
any transport node receiving it, but according to some specific rules:</p>
<ul>
<li><div class="line-block">
@ -590,7 +590,7 @@ announces is set at 2%, but can be configured on a per-interface basis.</div>
</li>
<li><div class="line-block">
<div class="line">If any given interface does not have enough bandwidth available for retransmitting the announce,
the announce will be assigned a priority inversely proportional to its hop count, and be inserted
the announce will be assigned a priority inversely proportional to its hop count, and be inserted
into a queue managed by the interface.</div>
</div>
</li>
@ -639,7 +639,7 @@ expect. Reticulum offers two ways to do this.</p>
<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>
an ECDH key exchange with the destinations public key, and encrypt the information.</div>
</div>
</li>
<li><div class="line-block">
@ -665,8 +665,8 @@ packet.</div>
<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
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>
@ -689,7 +689,7 @@ forward the packet will take note of this <em>link request</em>.</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
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>

View File

@ -557,7 +557,7 @@ execute commands on remote systems over Reticulum, and to view returned command
output.</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># Run rnx on the listening system, specifying which identities
# are allowed to execute commands
rncp --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
rnx --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
# From another system, run a command
rnx 7a55144adf826958a9529a3bcf08b149 &quot;cat /proc/cpuinfo&quot;