From 3a580e74de2b084a1d268f1fe167a8aad7937359 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 8 Sep 2024 14:55:07 +0200 Subject: [PATCH] Make ratchet IDs available to applications --- RNS/Destination.py | 8 ++++++-- RNS/Identity.py | 16 ++++++++++------ RNS/Link.py | 1 + RNS/Packet.py | 4 ++++ RNS/Transport.py | 14 +++++++------- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/RNS/Destination.py b/RNS/Destination.py index ec2ebb7..b389d14 100755 --- a/RNS/Destination.py +++ b/RNS/Destination.py @@ -154,6 +154,7 @@ class Destination: self.ratchet_interval = Destination.RATCHET_INTERVAL self.retained_ratchets = Destination.RATCHET_COUNT self.latest_ratchet_time = None + self.latest_ratchet_id = None self.__enforce_ratchets = False self.mtu = 0 @@ -401,6 +402,7 @@ class Destination: self.incoming_link_request(plaintext, packet) else: plaintext = self.decrypt(packet.data) + packet.ratchet_id = self.latest_ratchet_id if plaintext != None: if packet.packet_type == RNS.Packet.DATA: if self.callbacks.packet != None: @@ -565,7 +567,9 @@ class Destination: return plaintext if self.type == Destination.SINGLE and self.identity != None: - return self.identity.encrypt(plaintext, ratchet=RNS.Identity.get_ratchet(self.hash)) + selected_ratchet = RNS.Identity.get_ratchet(self.hash) + self.latest_ratchet_id = RNS.Identity.truncated_hash(selected_ratchet) + return self.identity.encrypt(plaintext, ratchet=selected_ratchet) if self.type == Destination.GROUP: if hasattr(self, "prv") and self.prv != None: @@ -588,7 +592,7 @@ class Destination: return ciphertext if self.type == Destination.SINGLE and self.identity != None: - return self.identity.decrypt(ciphertext, ratchets=self.ratchets, enforce_ratchets=self.__enforce_ratchets) + return self.identity.decrypt(ciphertext, ratchets=self.ratchets, enforce_ratchets=self.__enforce_ratchets, ratchet_id_receiver=self) if self.type == Destination.GROUP: if hasattr(self, "prv") and self.prv != None: diff --git a/RNS/Identity.py b/RNS/Identity.py index 82108ec..5697453 100644 --- a/RNS/Identity.py +++ b/RNS/Identity.py @@ -631,8 +631,6 @@ class Identity: ephemeral_pub_bytes = ephemeral_key.public_key().public_bytes() if ratchet != None: - # TODO: Remove at some point - RNS.log(f"Encrypting with ratchet {RNS.prettyhexrep(RNS.Identity.truncated_hash(ratchet))}", RNS.LOG_EXTREME) target_public_key = X25519PublicKey.from_public_bytes(ratchet) else: target_public_key = self.pub @@ -655,7 +653,7 @@ class Identity: raise KeyError("Encryption failed because identity does not hold a public key") - def decrypt(self, ciphertext_token, ratchets=None, enforce_ratchets=False): + def decrypt(self, ciphertext_token, ratchets=None, enforce_ratchets=False, ratchet_id_receiver=None): """ Decrypts information for the identity. @@ -675,6 +673,7 @@ class Identity: for ratchet in ratchets: try: ratchet_prv = X25519PrivateKey.from_private_bytes(ratchet) + ratchet_id = Identity.truncated_hash(ratchet_prv.public_key().public_bytes()) shared_key = ratchet_prv.exchange(peer_pub) derived_key = RNS.Cryptography.hkdf( length=32, @@ -685,9 +684,8 @@ class Identity: fernet = Fernet(derived_key) plaintext = fernet.decrypt(ciphertext) - - # TODO: Remove at some point - RNS.log(f"Decrypted with ratchet {RNS.prettyhexrep(RNS.Identity.truncated_hash(ratchet_prv.public_key().public_bytes()))}", RNS.LOG_EXTREME) + if ratchet_id_receiver: + ratchet_id_receiver.latest_ratchet_id = ratchet_id break @@ -696,6 +694,8 @@ class Identity: if enforce_ratchets and plaintext == None: RNS.log("Decryption with ratchet enforcement by "+RNS.prettyhexrep(self.hash)+" failed. Dropping packet.", RNS.LOG_DEBUG) + if ratchet_id_receiver: + ratchet_id_receiver.latest_ratchet_id = None return None if plaintext == None: @@ -709,9 +709,13 @@ class Identity: fernet = Fernet(derived_key) plaintext = fernet.decrypt(ciphertext) + if ratchet_id_receiver: + ratchet_id_receiver.latest_ratchet_id = None except Exception as e: RNS.log("Decryption by "+RNS.prettyhexrep(self.hash)+" failed: "+str(e), RNS.LOG_DEBUG) + if ratchet_id_receiver: + ratchet_id_receiver.latest_ratchet_id = None return plaintext; else: diff --git a/RNS/Link.py b/RNS/Link.py index 95fdb12..535dc3e 100644 --- a/RNS/Link.py +++ b/RNS/Link.py @@ -775,6 +775,7 @@ class Link: should_query = False if packet.context == RNS.Packet.NONE: plaintext = self.decrypt(packet.data) + packet.ratchet_id = self.link_id if plaintext != None: if self.callbacks.packet != None: thread = threading.Thread(target=self.callbacks.packet, args=(plaintext, packet)) diff --git a/RNS/Packet.py b/RNS/Packet.py index 4246aca..f470fae 100755 --- a/RNS/Packet.py +++ b/RNS/Packet.py @@ -140,6 +140,7 @@ class Packet: self.MTU = RNS.Reticulum.MTU self.sent_at = None self.packet_hash = None + self.ratchet_id = None self.attached_interface = attached_interface self.receiving_interface = None @@ -195,6 +196,8 @@ class Packet: # In all other cases, we encrypt the packet # with the destination's encryption method self.ciphertext = self.destination.encrypt(self.data) + if hasattr(self.destination, "latest_ratchet_id"): + self.ratchet_id = self.destination.latest_ratchet_id if self.header_type == Packet.HEADER_2: if self.transport_id != None: @@ -418,6 +421,7 @@ class PacketReceipt: except Exception as e: RNS.log("An error occurred while evaluating external delivery callback for "+str(link), RNS.LOG_ERROR) RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) + RNS.trace_exception(e) return True else: diff --git a/RNS/Transport.py b/RNS/Transport.py index f0a0248..5b2e4f3 100755 --- a/RNS/Transport.py +++ b/RNS/Transport.py @@ -52,16 +52,16 @@ class Transport: Maximum amount of hops that Reticulum will transport a packet. """ - PATHFINDER_R = 1 # Retransmit retries - PATHFINDER_G = 5 # Retry grace period - PATHFINDER_RW = 0.5 # Random window for announce rebroadcast - PATHFINDER_E = 60*60*24*7 # Path expiration of one week - AP_PATH_TIME = 60*60*24 # Path expiration of one day for Access Point paths - ROAMING_PATH_TIME = 60*60*6 # Path expiration of 6 hours for Roaming paths + PATHFINDER_R = 1 # Retransmit retries + PATHFINDER_G = 5 # Retry grace period + PATHFINDER_RW = 0.5 # Random window for announce rebroadcast + PATHFINDER_E = 60*60*24*7 # Path expiration of one week + AP_PATH_TIME = 60*60*24 # Path expiration of one day for Access Point paths + ROAMING_PATH_TIME = 60*60*6 # Path expiration of 6 hours for Roaming paths # TODO: Calculate an optimal number for this in # various situations - LOCAL_REBROADCASTS_MAX = 2 # How many local rebroadcasts of an announce is allowed + LOCAL_REBROADCASTS_MAX = 2 # How many local rebroadcasts of an announce is allowed PATH_REQUEST_TIMEOUT = 15 # Default timuout for client path requests in seconds PATH_REQUEST_GRACE = 0.4 # Grace time before a path announcement is made, allows directly reachable peers to respond first