mirror of
https://github.com/markqvist/Reticulum.git
synced 2024-12-22 11:50:18 +00:00
Started bundle class
This commit is contained in:
parent
4ffe4482d3
commit
e2122be006
@ -1056,8 +1056,9 @@ body .markdown-body
|
||||
.codehilite .il{color:#009999}
|
||||
.codehilite .gc{color:#999;background-color:#EAF2F5}
|
||||
</style><title>README</title></head><body><article class="markdown-body"><h1 id="reticulum-network-stack">Reticulum Network Stack α<a class="headerlink" href="#reticulum-network-stack" title="Permanent link"></a></h1>
|
||||
<p>Reticulum is a cryptography-based networking stack for high-latency, wide-area networks built on readily available hardware. Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, resource caching, unforgeable packet acknowledgements and much more.</p>
|
||||
<p>Reticulum is a complete networking stack, and does not use IP or higher layers, although it can be easily tunnelled through conventional IP networks. This frees up overhead, that has been utilised to implement a networking stack built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.</p>
|
||||
<p>Reticulum is a cryptography-based networking stack for high-latency, wide-area networks built on readily available hardware. Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.</p>
|
||||
<p>Reticulum is a complete networking stack, and does not use IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum.</p>
|
||||
<p>Having no dependencies on traditional networking stacks free up overhead that has been utilised to implement a networking stack built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.</p>
|
||||
<p>No kernel modules or drivers are required. Reticulum runs completely in userland, and can run on practically any system that runs Python 3.</p>
|
||||
<p>For more info, see <a href="https://unsigned.io/projects/reticulum/">unsigned.io/projects/reticulum</a></p>
|
||||
<h2 id="notable-features">Notable Features<a class="headerlink" href="#notable-features" title="Permanent link"></a></h2>
|
||||
@ -1066,7 +1067,7 @@ body .markdown-body
|
||||
<li>Fully self-configuring multi-hop routing</li>
|
||||
<li>Asymmetric RSA encryption and signatures as basis for all communication</li>
|
||||
<li>Perfect Forward Secrecy on links with ephemereal Elliptic Curve Diffie-Hellman keys (on the SECP256R1 curve)</li>
|
||||
<li>Reticulum uses the <a href="https://github.com/fernet/spec/blob/master/Spec.md">Fernet</a> specification for encryption to group destinations and on links<ul>
|
||||
<li>Reticulum uses the <a href="https://github.com/fernet/spec/blob/master/Spec.md">Fernet</a> specification for encryption on links and to group destinations<ul>
|
||||
<li>AES-128 in CBC mode with PKCS7 padding</li>
|
||||
<li>HMAC using SHA256 for authentication</li>
|
||||
<li>IVs are generated through os.urandom()</li>
|
||||
@ -1075,7 +1076,7 @@ body .markdown-body
|
||||
<li>Unforgeable packet delivery confirmations</li>
|
||||
<li>A variety of supported interface types</li>
|
||||
<li>Efficient and easy resource transfers</li>
|
||||
<li>A simple and easy-to-use API</li>
|
||||
<li>An intuitive and easy-to-use API</li>
|
||||
</ul>
|
||||
<h2 id="where-can-reticulum-be-used">Where can Reticulum be used?<a class="headerlink" href="#where-can-reticulum-be-used" title="Permanent link"></a></h2>
|
||||
<p>On practically any hardware that can support at least a half-duplex channel with 1.000 bits per second throughput, and an MTU of 500 bytes. Data radios, modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes, free-space optical links and similar systems are all examples of the types of interfaces Reticulum was designed for.</p>
|
||||
|
10
README.md
10
README.md
@ -1,9 +1,11 @@
|
||||
Reticulum Network Stack α
|
||||
==========
|
||||
|
||||
Reticulum is a cryptography-based networking stack for high-latency, wide-area networks built on readily available hardware. Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, resource caching, unforgeable packet acknowledgements and much more.
|
||||
Reticulum is a cryptography-based networking stack for high-latency, wide-area networks built on readily available hardware. Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.
|
||||
|
||||
Reticulum is a complete networking stack, and does not use IP or higher layers, although it can be easily tunnelled through conventional IP networks. This frees up overhead, that has been utilised to implement a networking stack built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.
|
||||
Reticulum is a complete networking stack, and does not use IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum.
|
||||
|
||||
Having no dependencies on traditional networking stacks free up overhead that has been utilised to implement a networking stack built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.
|
||||
|
||||
No kernel modules or drivers are required. Reticulum runs completely in userland, and can run on practically any system that runs Python 3.
|
||||
|
||||
@ -14,14 +16,14 @@ For more info, see [unsigned.io/projects/reticulum](https://unsigned.io/projects
|
||||
- Fully self-configuring multi-hop routing
|
||||
- Asymmetric RSA encryption and signatures as basis for all communication
|
||||
- Perfect Forward Secrecy on links with ephemereal Elliptic Curve Diffie-Hellman keys (on the SECP256R1 curve)
|
||||
- Reticulum uses the [Fernet](https://github.com/fernet/spec/blob/master/Spec.md) specification for encryption to group destinations and on links
|
||||
- Reticulum uses the [Fernet](https://github.com/fernet/spec/blob/master/Spec.md) specification for encryption on links and to group destinations
|
||||
- AES-128 in CBC mode with PKCS7 padding
|
||||
- HMAC using SHA256 for authentication
|
||||
- IVs are generated through os.urandom()
|
||||
- Unforgeable packet delivery confirmations
|
||||
- A variety of supported interface types
|
||||
- Efficient and easy resource transfers
|
||||
- A simple and easy-to-use API
|
||||
- An intuitive and easy-to-use API
|
||||
|
||||
## Where can Reticulum be used?
|
||||
On practically any hardware that can support at least a half-duplex channel with 1.000 bits per second throughput, and an MTU of 500 bytes. Data radios, modems, LoRa radios, serial lines, AX.25 TNCs, amateur radio digital modes, free-space optical links and similar systems are all examples of the types of interfaces Reticulum was designed for.
|
||||
|
0
RNS/Bundle.py
Normal file
0
RNS/Bundle.py
Normal file
@ -89,6 +89,7 @@ class Packet:
|
||||
self.packet_hash = None
|
||||
|
||||
self.attached_interface = attached_interface
|
||||
self.receiving_interface = None
|
||||
|
||||
def getPackedFlags(self):
|
||||
if self.context == Packet.LRPROOF:
|
||||
@ -127,9 +128,12 @@ class Packet:
|
||||
# Keepalive packets contain no actual
|
||||
# data
|
||||
self.ciphertext = self.data
|
||||
elif self.context == Packet.CACHE_REQUEST:
|
||||
# Cache-requests are not encrypted
|
||||
self.ciphertext = self.data
|
||||
else:
|
||||
# In all other cases, we encrypt the packet
|
||||
# with the destination's public key
|
||||
# with the destination's encryption method
|
||||
self.ciphertext = self.destination.encrypt(self.data)
|
||||
|
||||
if self.header_type == Packet.HEADER_2:
|
||||
|
@ -22,7 +22,7 @@ class Resource:
|
||||
#
|
||||
# A small system in this regard is
|
||||
# defined as a Raspberry Pi, which should
|
||||
# be able to compress, encrypt and hashmap
|
||||
# be able to compress, encrypt and hash-map
|
||||
# the resource in about 10 seconds.
|
||||
MAX_EFFICIENT_SIZE = 16 * 1024 * 1024
|
||||
|
||||
@ -308,7 +308,7 @@ class Resource:
|
||||
|
||||
if sleep_time < 0:
|
||||
if self.retries_left > 0:
|
||||
RNS.log("Timeout waiting for parts, requesting retry", RNS.LOG_DEBUG)
|
||||
RNS.log("Timed out waiting for parts, requesting retry", RNS.LOG_DEBUG)
|
||||
if self.window > self.window_min:
|
||||
self.window -= 1
|
||||
if self.window_max > self.window_min:
|
||||
@ -344,7 +344,7 @@ class Resource:
|
||||
expected_data = self.hash + self.expected_proof
|
||||
expected_proof_packet = RNS.Packet(self.link, expected_data, packet_type=RNS.Packet.PROOF, context=RNS.Packet.RESOURCE_PRF)
|
||||
expected_proof_packet.pack()
|
||||
RNS.Transport.cache_request(expected_proof_packet.packet_hash)
|
||||
RNS.Transport.cache_request(expected_proof_packet.packet_hash, self.link)
|
||||
self.last_part_sent = time.time()
|
||||
sleep_time = 0.001
|
||||
|
||||
|
102
RNS/Transport.py
102
RNS/Transport.py
@ -338,7 +338,7 @@ class Transport:
|
||||
new_raw += Transport.destination_table[packet.destination_hash][1]
|
||||
new_raw += packet.raw[2:]
|
||||
# TODO: Remove at some point
|
||||
RNS.log("Packet was inserted into transport via "+RNS.prettyhexrep(Transport.destination_table[packet.destination_hash][1])+" on: "+str(outbound_interface), RNS.LOG_EXTREME)
|
||||
# RNS.log("Packet was inserted into transport via "+RNS.prettyhexrep(Transport.destination_table[packet.destination_hash][1])+" on: "+str(outbound_interface), RNS.LOG_EXTREME)
|
||||
outbound_interface.processOutgoing(new_raw)
|
||||
Transport.destination_table[packet.destination_hash][0] = time.time()
|
||||
sent = True
|
||||
@ -359,7 +359,7 @@ class Transport:
|
||||
new_raw += Transport.destination_table[packet.destination_hash][1]
|
||||
new_raw += packet.raw[2:]
|
||||
# TODO: Remove at some point
|
||||
RNS.log("Packet was inserted into transport via "+RNS.prettyhexrep(Transport.destination_table[packet.destination_hash][1])+" on: "+str(outbound_interface), RNS.LOG_EXTREME)
|
||||
# RNS.log("Packet was inserted into transport via "+RNS.prettyhexrep(Transport.destination_table[packet.destination_hash][1])+" on: "+str(outbound_interface), RNS.LOG_EXTREME)
|
||||
outbound_interface.processOutgoing(new_raw)
|
||||
Transport.destination_table[packet.destination_hash][0] = time.time()
|
||||
sent = True
|
||||
@ -416,6 +416,7 @@ class Transport:
|
||||
Transport.jobs_locked = False
|
||||
return sent
|
||||
|
||||
|
||||
@staticmethod
|
||||
def packet_filter(packet):
|
||||
# TODO: Think long and hard about this.
|
||||
@ -429,8 +430,11 @@ class Transport:
|
||||
return True
|
||||
if packet.context == RNS.Packet.RESOURCE:
|
||||
return True
|
||||
if packet.context == RNS.Packet.CACHE_REQUEST:
|
||||
return True
|
||||
if packet.destination_type == RNS.Destination.PLAIN:
|
||||
return True
|
||||
|
||||
if not packet.packet_hash in Transport.packet_hashlist:
|
||||
return True
|
||||
else:
|
||||
@ -455,9 +459,6 @@ class Transport:
|
||||
RNS.log(str(interface)+" received packet with hash "+RNS.prettyhexrep(packet.packet_hash), RNS.LOG_EXTREME)
|
||||
|
||||
if len(Transport.local_client_interfaces) > 0:
|
||||
new_raw = packet.raw[0:1]
|
||||
new_raw += struct.pack("!B", packet.hops)
|
||||
new_raw += packet.raw[2:]
|
||||
|
||||
if Transport.is_local_client_interface(interface):
|
||||
packet.hops -= 1
|
||||
@ -506,10 +507,19 @@ class Transport:
|
||||
# shared instance, so they look directly reach-
|
||||
# able), and reinsert, so the normal transport
|
||||
# implementation can handle the packet.
|
||||
|
||||
if packet.transport_id == None and for_local_client:
|
||||
packet.transport_id = Transport.identity.hash
|
||||
|
||||
# If this is a cache request, and we can fullfill
|
||||
# it, do so and stop processing. Otherwise resume
|
||||
# normal processing.
|
||||
if packet.context == RNS.Packet.CACHE_REQUEST:
|
||||
if Transport.cache_request_packet(packet):
|
||||
return
|
||||
|
||||
# If the packet is in transport, check whether we
|
||||
# are the designated next hop, and process it
|
||||
# accordingly if we are.
|
||||
if packet.transport_id != None and packet.packet_type != RNS.Packet.ANNOUNCE:
|
||||
if packet.transport_id == Transport.identity.hash:
|
||||
RNS.log("Received packet in transport for "+RNS.prettyhexrep(packet.destination_hash)+" with matching transport ID, transporting it...", RNS.LOG_DEBUG)
|
||||
@ -563,10 +573,8 @@ class Transport:
|
||||
else:
|
||||
# TODO: There should probably be some kind of REJECT
|
||||
# mechanism here, to signal to the source that their
|
||||
# expected path failed
|
||||
# expected path failed.
|
||||
RNS.log("Got packet in transport, but no known path to final destination. Dropping packet.", RNS.LOG_DEBUG)
|
||||
else:
|
||||
pass
|
||||
|
||||
# Link transport handling. Directs packets according
|
||||
# to entries in the link tables
|
||||
@ -872,10 +880,8 @@ class Transport:
|
||||
|
||||
@staticmethod
|
||||
def shouldCache(packet):
|
||||
# TODO: Implement sensible rules for which
|
||||
# packets to cache
|
||||
#if packet.context == RNS.Packet.RESOURCE_PRF:
|
||||
# return True
|
||||
if packet.context == RNS.Packet.RESOURCE_PRF:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@ -889,10 +895,14 @@ class Transport:
|
||||
if RNS.Transport.shouldCache(packet) or force_cache:
|
||||
try:
|
||||
packet_hash = RNS.hexrep(packet.getHash(), delimit=False)
|
||||
file = open(RNS.Reticulum.cachepath+"/"+packet_hash, "wb")
|
||||
file.write(packet.raw)
|
||||
interface_reference = None
|
||||
if packet.receiving_interface != None:
|
||||
interface_reference = str(packet.receiving_interface)
|
||||
|
||||
file = open(RNS.Reticulum.cachepath+"/"+packet_hash, "wb")
|
||||
file.write(umsgpack.packb([packet.raw, interface_reference]))
|
||||
file.close()
|
||||
RNS.log("Wrote packet "+packet_hash+" to cache", RNS.LOG_EXTREME)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error writing packet to cache", RNS.LOG_ERROR)
|
||||
RNS.log("The contained exception was: "+str(e))
|
||||
@ -902,11 +912,19 @@ class Transport:
|
||||
try:
|
||||
packet_hash = RNS.hexrep(packet_hash, delimit=False)
|
||||
path = RNS.Reticulum.cachepath+"/"+packet_hash
|
||||
|
||||
if os.path.isfile(path):
|
||||
file = open(path, "rb")
|
||||
raw = file.read()
|
||||
cached_data = umsgpack.unpackb(file.read())
|
||||
file.close()
|
||||
packet = RNS.Packet(None, raw)
|
||||
|
||||
packet = RNS.Packet(None, cached_data[0])
|
||||
interface_reference = cached_data[1]
|
||||
|
||||
for interface in Transport.interfaces:
|
||||
if str(interface) == interface_reference:
|
||||
packet.receiving_interface = interface
|
||||
|
||||
return packet
|
||||
else:
|
||||
return None
|
||||
@ -914,33 +932,34 @@ class Transport:
|
||||
RNS.log("Exception occurred while getting cached packet.", RNS.LOG_ERROR)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
# TODO: Implement cache requests. Needs methodology
|
||||
# rethinking. This is skeleton code.
|
||||
@staticmethod
|
||||
def cache_request_packet(packet):
|
||||
if len(packet.data) == RNS.Identity.HASHLENGTH/8:
|
||||
packet_hash = RNS.hexrep(packet.data, delimit=False)
|
||||
packet = Transport.get_cached_packet(packet_hash)
|
||||
packet = Transport.get_cached_packet(packet.data)
|
||||
|
||||
if packet != None:
|
||||
# TODO: Implement outbound for this
|
||||
pass
|
||||
# If the packet was retrieved from the local
|
||||
# cache, replay it to the Transport instance,
|
||||
# so that it can be directed towards it original
|
||||
# destination.
|
||||
Transport.inbound(packet.raw, packet.receiving_interface)
|
||||
return True
|
||||
else:
|
||||
pass
|
||||
|
||||
# TODO: Implement cache requests. Needs methodology
|
||||
# rethinking. This is skeleton code.
|
||||
@staticmethod
|
||||
def cache_request(packet_hash):
|
||||
RNS.log("Cache request for "+RNS.prettyhexrep(packet_hash), RNS.LOG_EXTREME)
|
||||
path = RNS.Reticulum.cachepath+"/"+RNS.hexrep(packet_hash, delimit=False)
|
||||
if os.path.isfile(path):
|
||||
file = open(path, "rb")
|
||||
raw = file.read()
|
||||
Transport.inbound(raw)
|
||||
file.close()
|
||||
return False
|
||||
else:
|
||||
cache_request_packet = RNS.Packet(Transport.transport_destination(), packet_hash, context = RNS.Packet.CACHE_REQUEST)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def cache_request(packet_hash, destination):
|
||||
cached_packet = Transport.get_cached_packet(packet_hash)
|
||||
if cached_packet:
|
||||
# The packet was found in the local cache,
|
||||
# replay it to the Transport instance.
|
||||
Transport.inbound(packet.raw, packet.receiving_interface)
|
||||
else:
|
||||
# The packet is not in the local cache,
|
||||
# query the network.
|
||||
RNS.Packet(destination, packet_hash, context = RNS.Packet.CACHE_REQUEST).send()
|
||||
|
||||
@staticmethod
|
||||
def hasPath(destination_hash):
|
||||
@ -1026,13 +1045,6 @@ class Transport:
|
||||
else:
|
||||
RNS.log("No known path to requested destination, ignoring request", RNS.LOG_DEBUG)
|
||||
|
||||
# TODO: Currently only used for cache requests.
|
||||
# Needs rethink.
|
||||
@staticmethod
|
||||
def transport_destination():
|
||||
# TODO: implement this
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def from_local_client(packet):
|
||||
if hasattr(packet.receiving_interface, "parent_interface"):
|
||||
|
Loading…
Reference in New Issue
Block a user