From bab779a34cc9b1895f3b1df1ae0e2f8ef3cfb2c1 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 13 Jun 2023 16:10:47 +0200 Subject: [PATCH] Fixed race condition for link initiators on timed out link establishment --- RNS/Link.py | 83 +++++++++++++++++++++++++++--------------------- RNS/Transport.py | 2 ++ RNS/_version.py | 2 +- 3 files changed, 49 insertions(+), 38 deletions(-) diff --git a/RNS/Link.py b/RNS/Link.py index 162f820..0b7b856 100644 --- a/RNS/Link.py +++ b/RNS/Link.py @@ -264,41 +264,50 @@ class Link: self.had_outbound() def validate_proof(self, packet): - if self.status == Link.PENDING: - if self.initiator and len(packet.data) == RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2: - peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2] - peer_sig_pub_bytes = self.destination.identity.get_public_key()[Link.ECPUBSIZE//2:Link.ECPUBSIZE] - self.load_peer(peer_pub_bytes, peer_sig_pub_bytes) - self.handshake() + try: + if self.status == Link.PENDING: + if self.initiator and len(packet.data) == RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2: + peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2] + peer_sig_pub_bytes = self.destination.identity.get_public_key()[Link.ECPUBSIZE//2:Link.ECPUBSIZE] + self.load_peer(peer_pub_bytes, peer_sig_pub_bytes) + self.handshake() - self.establishment_cost += len(packet.raw) - signed_data = self.link_id+self.peer_pub_bytes+self.peer_sig_pub_bytes - signature = packet.data[:RNS.Identity.SIGLENGTH//8] - - if self.destination.identity.validate(signature, signed_data): - self.rtt = time.time() - self.request_time - self.attached_interface = packet.receiving_interface - self.__remote_identity = self.destination.identity - self.status = Link.ACTIVE - self.activated_at = time.time() - self.last_proof = self.activated_at - RNS.Transport.activate_link(self) - RNS.log("Link "+str(self)+" established with "+str(self.destination)+", RTT is "+str(round(self.rtt, 3))+"s", RNS.LOG_VERBOSE) + self.establishment_cost += len(packet.raw) + signed_data = self.link_id+self.peer_pub_bytes+self.peer_sig_pub_bytes + signature = packet.data[:RNS.Identity.SIGLENGTH//8] - if self.rtt != None and self.establishment_cost != None and self.rtt > 0 and self.establishment_cost > 0: - self.establishment_rate = self.establishment_cost/self.rtt + if self.destination.identity.validate(signature, signed_data): + if self.status != Link.PENDING: + raise IOError("Invalid link state for proof validation") - rtt_data = umsgpack.packb(self.rtt) - rtt_packet = RNS.Packet(self, rtt_data, context=RNS.Packet.LRRTT) - rtt_packet.send() - self.had_outbound() + self.rtt = time.time() - self.request_time + self.attached_interface = packet.receiving_interface + self.__remote_identity = self.destination.identity + self.status = Link.ACTIVE + self.activated_at = time.time() + self.last_proof = self.activated_at + RNS.Transport.activate_link(self) + RNS.log("Link "+str(self)+" established with "+str(self.destination)+", RTT is "+str(round(self.rtt, 3))+"s", RNS.LOG_VERBOSE) + + if self.rtt != None and self.establishment_cost != None and self.rtt > 0 and self.establishment_cost > 0: + self.establishment_rate = self.establishment_cost/self.rtt - if self.callbacks.link_established != None: - thread = threading.Thread(target=self.callbacks.link_established, args=(self,)) - thread.daemon = True - thread.start() - else: - RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG) + rtt_data = umsgpack.packb(self.rtt) + rtt_packet = RNS.Packet(self, rtt_data, context=RNS.Packet.LRRTT) + rtt_packet.send() + self.had_outbound() + + if self.callbacks.link_established != None: + thread = threading.Thread(target=self.callbacks.link_established, args=(self,)) + thread.daemon = True + thread.start() + else: + RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG) + + except Exception as e: + self.status = Link.CLOSED + RNS.log("An error ocurred while validating link request proof on "+str(self)+".", RNS.LOG_ERROR) + RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) def identify(self, identity): @@ -520,16 +529,16 @@ class Link: next_check = self.request_time + self.establishment_timeout sleep_time = next_check - time.time() if time.time() >= self.request_time + self.establishment_timeout: - if self.initiator: - RNS.log("Timeout waiting for link request proof", RNS.LOG_DEBUG) - else: - RNS.log("Timeout waiting for RTT packet from link initiator", RNS.LOG_DEBUG) - self.status = Link.CLOSED self.teardown_reason = Link.TIMEOUT self.link_closed() sleep_time = 0.001 + if self.initiator: + RNS.log("Timeout waiting for link request proof", RNS.LOG_DEBUG) + else: + RNS.log("Timeout waiting for RTT packet from link initiator", RNS.LOG_DEBUG) + elif self.status == Link.ACTIVE: activated_at = self.activated_at if self.activated_at != None else 0 last_inbound = max(max(self.last_inbound, self.last_proof), activated_at) @@ -847,7 +856,7 @@ class Link: try: self.fernet = Fernet(self.derived_key) except Exception as e: - RNS.log("Could not "+str(self)+" instantiate Fernet while performin encryption on link. The contained exception was: "+str(e), RNS.LOG_ERROR) + RNS.log("Could not instantiate Fernet while performin encryption on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) raise e return self.fernet.encrypt(plaintext) diff --git a/RNS/Transport.py b/RNS/Transport.py index 6733180..e9c8d8a 100755 --- a/RNS/Transport.py +++ b/RNS/Transport.py @@ -1741,6 +1741,8 @@ class Transport: def activate_link(link): RNS.log("Activating link "+str(link), RNS.LOG_EXTREME) if link in Transport.pending_links: + if link.status != Link.PENDING: + raise IOError("Invalid link state for link activation") Transport.pending_links.remove(link) Transport.active_links.append(link) link.status = RNS.Link.ACTIVE diff --git a/RNS/_version.py b/RNS/_version.py index 6b27eee..86716a7 100644 --- a/RNS/_version.py +++ b/RNS/_version.py @@ -1 +1 @@ -__version__ = "0.5.4" +__version__ = "0.5.5"