Implemented interface authentication and virtual network segmentation

This commit is contained in:
Mark Qvist 2022-04-27 19:00:09 +02:00
parent b701cdd07f
commit 5d90ea565a
4 changed files with 117 additions and 28 deletions

View File

@ -21,6 +21,11 @@
# SOFTWARE.
from .vendor.platformutils import get_platform
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.backends import default_backend
cio_default_backend = default_backend()
if get_platform() == "android":
from .Interfaces import Interface
@ -122,6 +127,7 @@ class Reticulum:
HEADER_MINSIZE = 2+1+(TRUNCATED_HASHLENGTH//8)*1
HEADER_MAXSIZE = 2+1+(TRUNCATED_HASHLENGTH//8)*2
IFAC_MIN_SIZE = 1
IFAC_SALT = bytes.fromhex("adf54d882c9a9b80771eb4995d702d4a3e733391b2a0f53f416d9f907e55cff8")
MDU = MTU - HEADER_MAXSIZE - IFAC_MIN_SIZE
@ -186,6 +192,8 @@ class Reticulum:
self.share_instance = True
self.rpc_listener = None
self.ifac_salt = Reticulum.IFAC_SALT
self.requested_loglevel = loglevel
if self.requested_loglevel != None:
if self.requested_loglevel > RNS.LOG_EXTREME:
@ -356,14 +364,20 @@ class Reticulum:
ifac_size = c.as_int("ifac_size")
ifac_netname = None
if "ifac_netname" in c:
if c.as_int("ifac_netname") >= Reticulum.IFAC_MIN_SIZE:
ifac_netname = c.as_int("ifac_netname")
if "networkname" in c:
if c["networkname"] != "":
ifac_netname = c["networkname"]
if "network_name" in c:
if c["network_name"] != "":
ifac_netname = c["network_name"]
ifac_netkey = None
if "ifac_netkey" in c:
if c.as_int("ifac_netkey") >= Reticulum.IFAC_MIN_SIZE:
ifac_netkey = c.as_int("ifac_netkey")
if "passphrase" in c:
if c["passphrase"] != "":
ifac_netkey = c["passphrase"]
if "pass_phrase" in c:
if c["pass_phrase"] != "":
ifac_netkey = c["pass_phrase"]
configured_bitrate = None
if "bitrate" in c:
@ -406,8 +420,6 @@ class Reticulum:
interface.mode = interface_mode
RNS.Transport.interfaces.append(interface)
interface.announce_cap = announce_cap
if configured_bitrate:
interface.bitrate = configured_bitrate
@ -452,8 +464,6 @@ class Reticulum:
interface.mode = interface_mode
RNS.Transport.interfaces.append(interface)
interface.announce_cap = announce_cap
if configured_bitrate:
interface.bitrate = configured_bitrate
@ -492,8 +502,6 @@ class Reticulum:
interface.mode = interface_mode
RNS.Transport.interfaces.append(interface)
interface.announce_cap = announce_cap
if configured_bitrate:
interface.bitrate = configured_bitrate
@ -529,8 +537,6 @@ class Reticulum:
interface.mode = interface_mode
RNS.Transport.interfaces.append(interface)
interface.announce_cap = announce_cap
if configured_bitrate:
interface.bitrate = configured_bitrate
@ -562,8 +568,6 @@ class Reticulum:
interface.mode = interface_mode
RNS.Transport.interfaces.append(interface)
interface.announce_cap = announce_cap
if configured_bitrate:
interface.bitrate = configured_bitrate
@ -599,8 +603,6 @@ class Reticulum:
interface.mode = interface_mode
RNS.Transport.interfaces.append(interface)
interface.announce_cap = announce_cap
if configured_bitrate:
interface.bitrate = configured_bitrate
@ -650,8 +652,6 @@ class Reticulum:
interface.mode = interface_mode
RNS.Transport.interfaces.append(interface)
interface.announce_cap = announce_cap
if configured_bitrate:
interface.bitrate = configured_bitrate
@ -702,8 +702,6 @@ class Reticulum:
interface.mode = interface_mode
RNS.Transport.interfaces.append(interface)
interface.announce_cap = announce_cap
if configured_bitrate:
interface.bitrate = configured_bitrate
@ -748,8 +746,6 @@ class Reticulum:
interface.mode = interface_mode
RNS.Transport.interfaces.append(interface)
interface.announce_cap = announce_cap
if configured_bitrate:
interface.bitrate = configured_bitrate
@ -762,6 +758,27 @@ class Reticulum:
interface.ifac_netname = ifac_netname
interface.ifac_netkey = ifac_netkey
if interface.ifac_netname != None or interface.ifac_netkey != None:
ifac_origin = b""
if interface.ifac_netname != None:
ifac_origin += RNS.Identity.full_hash(interface.ifac_netname.encode("utf-8"))
if interface.ifac_netkey != None:
ifac_origin += RNS.Identity.full_hash(interface.ifac_netkey.encode("utf-8"))
ifac_origin_hash = RNS.Identity.full_hash(ifac_origin)
interface.ifac_key = HKDF(
algorithm=hashes.SHA256(),
length=64,
salt=self.ifac_salt,
info=None,
backend=cio_default_backend,
).derive(ifac_origin_hash)
interface.ifac_identity = RNS.Identity.from_bytes(interface.ifac_key)
interface.ifac_signature = interface.ifac_identity.sign(RNS.Identity.full_hash(interface.ifac_key))
RNS.Transport.interfaces.append(interface)
else:
@ -859,6 +876,15 @@ class Reticulum:
else:
ifstats["peers"] = None
if hasattr(interface, "ifac_signature"):
ifstats["ifac_signature"] = interface.ifac_signature
ifstats["ifac_size"] = interface.ifac_size
ifstats["ifac_netname"] = interface.ifac_netname
else:
ifstats["ifac_signature"] = None
ifstats["ifac_size"] = None
ifstats["ifac_netname"] = None
if hasattr(interface, "announce_queue"):
if interface.announce_queue != None:
ifstats["announce_queue"] = len(interface.announce_queue)

View File

@ -474,7 +474,23 @@ class Transport:
@staticmethod
def transmit(interface, raw):
interface.processOutgoing(raw)
try:
if hasattr(interface, "ifac_identity") and interface.ifac_identity != None:
# Calculate packet access code
ifac = interface.ifac_identity.sign(raw)[-interface.ifac_size:]
# Set IFAC flag
new_header = bytes([raw[0] | 0x80, raw[1]])
# Assemble new payload with IFAC and send it
new_raw = new_header+ifac+raw[2:]
interface.processOutgoing(new_raw)
else:
interface.processOutgoing(raw)
except Exception as e:
RNS.log("Error while transmitting on "+str(interface)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
@staticmethod
def outbound(packet):
@ -704,6 +720,44 @@ class Transport:
@staticmethod
def inbound(raw, interface=None):
# If interface access codes are enabled,
# we must authenticate each packet.
if interface != None and hasattr(interface, "ifac_identity") and interface.ifac_identity != None:
# Check that IFAC flag is set
if raw[0] & 0x80 == 0x80:
if len(raw) > 2+interface.ifac_size:
# Extract IFAC
ifac = raw[2:2+interface.ifac_size]
# Unset IFAC flag
new_header = bytes([raw[0] & 0x7f, raw[1]])
# Re-assemble packet
new_raw = new_header+raw[2+interface.ifac_size:]
# Calculate expected IFAC
expected_ifac = interface.ifac_identity.sign(new_raw)[-interface.ifac_size:]
# Check it
if ifac == expected_ifac:
# TODO: Remove log statements
RNS.log("Packet IFAC match, allowing", RNS.LOG_EXTREME)
raw = new_raw
else:
# TODO: Remove log statements
RNS.log("Packet IFAC mismatch, dropping packet", RNS.LOG_EXTREME)
return
else:
return
else:
# If the IFAC flag is not set, but should be,
# we drop the packet.
# TODO: Remove log statements
RNS.log(str(interface)+" with IFAC enabled received packet without access code, dropping.", RNS.LOG_EXTREME)
return
while (Transport.jobs_running):
sleep(0.01)

View File

@ -92,6 +92,10 @@ def program_setup(configdir, dispall=False, verbosity = 0):
clients = None
print(" {n}".format(n=ifstat["name"]))
if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
print(" Status : {ss}".format(ss=ss))
if clients != None:
@ -105,6 +109,10 @@ def program_setup(configdir, dispall=False, verbosity = 0):
if "peers" in ifstat and ifstat["peers"] != None:
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None:
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))

View File

@ -60,7 +60,8 @@ logfile = None
logdest = LOG_STDOUT
logtimefmt = "%Y-%m-%d %H:%M:%S"
random.seed(os.urandom(10))
instance_random = random.Random()
instance_random.seed(os.urandom(10))
_always_override_destination = False
@ -130,7 +131,7 @@ def log(msg, level=3, _override_destination = False):
def rand():
result = random.random()
result = instance_random.random()
return result
def hexrep(data, delimit=True):