From acc0f94e498f66a34a861182c365de17b464d364 Mon Sep 17 00:00:00 2001 From: acetone <63557806+freeacetone@users.noreply.github.com> Date: Fri, 19 Jun 2026 21:43:24 +0300 Subject: [PATCH] refresh offline-signed transient on destination re-create A repeated SESSION CREATE (SAM/I2CP) presenting a fresh offline-signature transient of the same master resolves to the same identity hash, so CreateNewLocalDestination returns the existing destination and the new transient is dropped: the destination keeps signing LeaseSets with the old transient and goes offline at its expiry. Adopt the newer transient in place (swap keys and republish the LeaseSet, without touching tunnels or streams) when its expiry is later, so an offline transient can be rotated before it expires without rebuilding the destination. --- libi2pd/Destination.cpp | 17 +++++++++++++++++ libi2pd/Destination.h | 3 ++- libi2pd_client/ClientContext.cpp | 4 ++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 5e790290..eca0ddd1 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -1167,6 +1167,23 @@ namespace client if (m_StreamingDestination) m_StreamingDestination->Start (); } + void ClientDestination::UpdateOfflineSignature (const i2p::data::PrivateKeys& keys) + { + // Adopt a newer offline transient of the same identity in place: swap keys and + // republish the LeaseSet, keeping tunnels and streams (unlike SetPrivateKeys). + if (!keys.IsOfflineSignature () || !m_Keys.IsOfflineSignature ()) return; + if (keys.GetPublic ()->GetIdentHash () != GetIdentHash ()) return; + const uint32_t expires = bufbe32toh (keys.GetOfflineSignature ().data ()); + if (expires <= bufbe32toh (m_Keys.GetOfflineSignature ().data ())) return; // only move forward + LogPrint (eLogInfo, "Destination: Refreshing offline signature for ", + GetIdentHash ().ToBase32 (), ", transient expires ", expires); + boost::asio::post (GetService (), [s = GetSharedFromThis (), keys]() + { + s->m_Keys = keys; + s->UpdateLeaseSet (); + }); + } + void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len, i2p::garlic::ECIESX25519AEADRatchetSession * from) { diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 579ba7d4..82cda560 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -186,10 +186,10 @@ namespace client // I2CP virtual void HandleDataMessage (const uint8_t * buf, size_t len, i2p::garlic::ECIESX25519AEADRatchetSession * from) = 0; virtual void CreateNewLeaseSet (const std::vector >& tunnels) = 0; + void UpdateLeaseSet (); private: - void UpdateLeaseSet (); std::shared_ptr GetLeaseSetMt (); void Publish (); void HandlePublishConfirmationTimer (const boost::system::error_code& ecode); @@ -252,6 +252,7 @@ namespace client const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; void SetPrivateKeys (const i2p::data::PrivateKeys& keys); + void UpdateOfflineSignature (const i2p::data::PrivateKeys& keys); void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; // ref counter diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index 9cabe30a..3f15e41a 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -410,6 +410,8 @@ namespace client if (it != m_Destinations.end ()) { LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists"); + if (keys.IsOfflineSignature ()) + it->second->UpdateOfflineSignature (keys); it->second->Start (); // make sure to start return it->second; } @@ -425,6 +427,8 @@ namespace client if (it != m_Destinations.end ()) { LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists"); + if (keys.IsOfflineSignature ()) + it->second->UpdateOfflineSignature (keys); it->second->Start (); // make sure to start return it->second; }