mirror of
https://github.com/markqvist/Reticulum.git
synced 2024-11-22 21:50:18 +00:00
Compare commits
No commits in common. "6fac96ec18b927cd14e784e862d864e11678c473" and "3b2fbe02c6333cdd3e1d3cf82d884ce34e516397" have entirely different histories.
6fac96ec18
...
3b2fbe02c6
@ -586,42 +586,12 @@ 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
|
||||
# Assemble new payload with IFAC and send it
|
||||
new_raw = new_header+ifac+raw[2:]
|
||||
|
||||
# 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)
|
||||
interface.processOutgoing(new_raw)
|
||||
|
||||
else:
|
||||
interface.processOutgoing(raw)
|
||||
@ -935,26 +905,6 @@ 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]])
|
||||
|
||||
|
@ -1,465 +0,0 @@
|
||||
#!/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()
|
@ -1 +1 @@
|
||||
__version__ = "0.4.8"
|
||||
__version__ = "0.4.7"
|
||||
|
@ -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
|
||||
- [x] Add `rnid` utility
|
||||
- [x] Add `rnsign` utility
|
||||
- [x] Add `rncrypt` utility
|
||||
- [ ] Add `rnid` utility
|
||||
- [ ] Add `rnsign` utility
|
||||
- [ ] 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.
@ -1,4 +0,0 @@
|
||||
********************************************
|
||||
An Explanation of Reticulum for Human Beings
|
||||
********************************************
|
||||
|
@ -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 get an example
|
||||
configuration file with all options via ``rnsd --exampleconfig``.
|
||||
default is located at ``~/.reticulum/config``. You can edit this file by hand,
|
||||
or use the interactive ``rnsconfig`` utility.
|
||||
|
||||
When Reticulum is started for the first time, it will create a default
|
||||
configuration file, with one active interface. This default interface uses
|
||||
|
@ -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 it’s
|
||||
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 it’s
|
||||
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 appending the public key,
|
||||
is represented. The uniquely identifying aspect is always achieved by the 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 it’s 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 its reachability, a destination simply needs to send an announce on any
|
||||
still become reachable. To update it's 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 a Reticulum instance, it will be forwarded by
|
||||
When an *announce* for a destination is transmitted by from 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 its hop count, and be inserted
|
||||
the announce will be assigned a priority inversely proportional to it's 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 destination's public key, and encrypt the information.
|
||||
an ECDH key exchange with the destinations public key, and encrypt the information.
|
||||
|
||||
* | It is important to note that this key exchange does not require any network traffic. The sender already
|
||||
knows the public key of the destination from an earlier received *announce*, and can thus perform the ECDH
|
||||
@ -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 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
|
||||
and signing this hash with it's Ed25519 signing key. Transport nodes in the network can then direct this
|
||||
*proof* back to the packets origin, where the signature can be verified against the destinations known
|
||||
public signing key.
|
||||
|
||||
* | In case the packet is addressed to a *group* destination type, the packet will be encrypted with the
|
||||
@ -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 it’s 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.
|
||||
|
||||
|
@ -377,7 +377,7 @@ output.
|
||||
|
||||
# Run rnx on the listening system, specifying which identities
|
||||
# are allowed to execute commands
|
||||
rnx --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
|
||||
rncp --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
|
||||
|
||||
# From another system, run a command
|
||||
rnx 7a55144adf826958a9529a3bcf08b149 "cat /proc/cpuinfo"
|
||||
|
@ -1,270 +0,0 @@
|
||||
<!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 © 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>
|
@ -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 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>
|
||||
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>
|
||||
<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
@ -337,12 +337,12 @@ needs to be purchased.</p>
|
||||
</section>
|
||||
<section id="introduction-basic-functionality">
|
||||
<span id="understanding-basicfunctionality"></span><h2>Introduction & 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 it’s
|
||||
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 it’s
|
||||
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 appending the public key,
|
||||
is represented. The uniquely identifying aspect is always achieved by the 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 it’s 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 it’s 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 a Reticulum instance, it will be forwarded by
|
||||
<p>When an <em>announce</em> for a destination is transmitted by from 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 it’s 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 destination’s 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 destination’s known
|
||||
and signing this hash with it’s 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 it’s 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>
|
||||
|
@ -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
|
||||
rnx --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
|
||||
rncp --listen -a 941bed5e228775e5a8079fc38b1ccf3f -a 1b03013c25f1c2ca068a4f080b844a10
|
||||
|
||||
# From another system, run a command
|
||||
rnx 7a55144adf826958a9529a3bcf08b149 "cat /proc/cpuinfo"
|
||||
|
Loading…
Reference in New Issue
Block a user