Started bundle class

This commit is contained in:
Mark Qvist 2020-06-14 18:33:01 +02:00
parent 4ffe4482d3
commit e2122be006
6 changed files with 76 additions and 57 deletions

View File

@ -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>

View File

@ -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
View File

View 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:

View File

@ -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

View File

@ -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"):