diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 1b317121..1347b5b8 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -29,7 +29,7 @@ namespace i2p { namespace tunnel { - Tunnel::Tunnel (std::shared_ptr config): + Tunnel::Tunnel (std::shared_ptr config): TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()), m_Config (config), m_IsShortBuildMessage (false), m_Pool (nullptr), m_State (eTunnelStatePending), m_FarEndTransports (i2p::data::RouterInfo::eAllTransports), @@ -44,7 +44,13 @@ namespace tunnel void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel) { auto numHops = m_Config->GetNumHops (); - const int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS; + bool insertPhonyRecord = m_Config->IsInbound() && numHops < MAX_NUM_RECORDS; + if (insertPhonyRecord) + { + m_Config->CreatePhonyHop (); + numHops++; + } + int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS; auto msg = numRecords <= STANDARD_NUM_RECORDS ? NewI2NPShortMessage () : NewI2NPMessage (); *msg->GetPayload () = numRecords; const size_t recordSize = m_Config->IsShort () ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; @@ -52,8 +58,7 @@ namespace tunnel // shuffle records std::vector recordIndicies; for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); - std::shuffle (recordIndicies.begin(), recordIndicies.end(), m_Pool ? m_Pool->GetRng () : std::mt19937(std::random_device()())); - + std::shuffle (recordIndicies.begin(), recordIndicies.end(), m_Pool ? m_Pool->GetRng () : std::mt19937(std::random_device()())); // create real records uint8_t * records = msg->GetPayload () + 1; TunnelHopConfig * hop = m_Config->GetFirstHop (); @@ -61,7 +66,7 @@ namespace tunnel while (hop) { uint32_t msgID; - if (hop->next) // we set replyMsgID for last hop only + if (hop->next && hop->next->ident) // we set replyMsgID for last non-phony hop only RAND_bytes ((uint8_t *)&msgID, 4); else msgID = replyMsgID; @@ -89,6 +94,9 @@ namespace tunnel } hop = hop->prev; } + // delete phony hop after encryption + if (insertPhonyRecord) m_Config->DeletePhonyHop (); + msg->FillI2NPMessageHeader (m_Config->IsShort () ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild); auto s = shared_from_this (); msg->onDrop = [s]() diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index 5d21cd8b..78e2d124 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -81,12 +81,12 @@ namespace tunnel /** function for visiting a hops stored in a tunnel */ typedef std::function)> TunnelHopVisitor; - Tunnel (std::shared_ptr config); + Tunnel (std::shared_ptr config); ~Tunnel (); void Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel = nullptr); - std::shared_ptr GetTunnelConfig () const { return m_Config; } + std::shared_ptr GetTunnelConfig () const { return m_Config; } std::vector > GetPeers () const; std::vector > GetInvertedPeers () const; bool IsShortBuildMessage () const { return m_IsShortBuildMessage; }; @@ -125,7 +125,7 @@ namespace tunnel private: - std::shared_ptr m_Config; + std::shared_ptr m_Config; std::vector m_Hops; bool m_IsShortBuildMessage; std::shared_ptr m_Pool; // pool, tunnel belongs to, or null @@ -139,7 +139,7 @@ namespace tunnel { public: - OutboundTunnel (std::shared_ptr config): + OutboundTunnel (std::shared_ptr config): Tunnel (config), m_Gateway (*this), m_EndpointIdentHash (config->GetLastIdentHash ()) {}; void SendTunnelDataMsgTo (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg); @@ -164,7 +164,7 @@ namespace tunnel { public: - InboundTunnel (std::shared_ptr config): Tunnel (config), m_Endpoint (true) {}; + InboundTunnel (std::shared_ptr config): Tunnel (config), m_Endpoint (true) {}; void HandleTunnelDataMsg (std::shared_ptr&& msg) override; virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; bool IsInbound() const override { return true; } diff --git a/libi2pd/TunnelConfig.cpp b/libi2pd/TunnelConfig.cpp index fe0e8573..1ae8f3e9 100644 --- a/libi2pd/TunnelConfig.cpp +++ b/libi2pd/TunnelConfig.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2025, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -219,6 +219,42 @@ namespace tunnel return tag; } + void LongPhonyTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) + { + uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE; + memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetIdentHash (), 16); + memcpy (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, i2p::transport::transports.GetNextX25519KeysPair ()->GetPublicKey (), 32); + RAND_bytes (record + 48, TUNNEL_BUILD_RECORD_SIZE - 48); + } + + void ShortPhonyTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) + { + uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE; + memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetIdentHash (), 16); + memcpy (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, i2p::transport::transports.GetNextX25519KeysPair ()->GetPublicKey (), 32); + RAND_bytes (record + 48, SHORT_TUNNEL_BUILD_RECORD_SIZE - 48); + } + + TunnelConfig::TunnelConfig (const std::vector >& peers, + bool isShort, i2p::data::RouterInfo::CompatibleTransports farEndTransports): + m_IsShort (isShort), m_FarEndTransports (farEndTransports) + { + // inbound + CreatePeers (peers); + m_LastHop->SetNextIdent (i2p::context.GetIdentHash ()); + } + + TunnelConfig::TunnelConfig (const std::vector >& peers, + uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent, bool isShort, + i2p::data::RouterInfo::CompatibleTransports farEndTransports): + m_IsShort (isShort), m_FarEndTransports (farEndTransports) + { + // outbound + CreatePeers (peers); + m_FirstHop->isGateway = false; + m_LastHop->SetReplyHop (replyTunnelID, replyIdent); + } + void TunnelConfig::CreatePeers (const std::vector >& peers) { TunnelHopConfig * prev = nullptr; @@ -245,5 +281,35 @@ namespace tunnel } m_LastHop = prev; } + + void TunnelConfig::CreatePhonyHop () + { + if (m_LastHop && m_LastHop->ident) + { + TunnelHopConfig * hop = nullptr; + if (m_IsShort) + hop = new ShortPhonyTunnelHopConfig (); + else + hop = new LongPhonyTunnelHopConfig (); + if (hop) + { + hop->prev = m_LastHop; + m_LastHop->next = hop; + m_LastHop = hop; + } + } + } + + void TunnelConfig::DeletePhonyHop () + { + if (m_LastHop && !m_LastHop->ident) + { + if (m_LastHop->prev) m_LastHop->prev->next = nullptr; + else m_FirstHop = nullptr; + auto tmp = m_LastHop; + m_LastHop = m_LastHop->prev; + delete tmp; + } + } } } \ No newline at end of file diff --git a/libi2pd/TunnelConfig.h b/libi2pd/TunnelConfig.h index 718a6fdb..a024f359 100644 --- a/libi2pd/TunnelConfig.h +++ b/libi2pd/TunnelConfig.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2024, The PurpleI2P Project +* Copyright (c) 2013-2025, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -77,28 +77,35 @@ namespace tunnel uint64_t GetGarlicKey (uint8_t * key) const override; }; + struct PhonyTunnelHopConfig: public ECIESTunnelHopConfig + { + PhonyTunnelHopConfig (): ECIESTunnelHopConfig (nullptr) {} + uint8_t GetRetCode (const uint8_t * records) const override { return 0; } + bool DecryptBuildResponseRecord (uint8_t * records) const override { return true; } + void DecryptRecord (uint8_t * records, int index) const override {} // do nothing + }; + + struct LongPhonyTunnelHopConfig: public PhonyTunnelHopConfig + { + void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override; + }; + + struct ShortPhonyTunnelHopConfig: public PhonyTunnelHopConfig + { + void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override; + }; + class TunnelConfig { public: TunnelConfig (const std::vector >& peers, - bool isShort, i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports): // inbound - m_IsShort (isShort), m_FarEndTransports (farEndTransports) - { - CreatePeers (peers); - m_LastHop->SetNextIdent (i2p::context.GetIdentHash ()); - } + bool isShort, i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports); // inbound TunnelConfig (const std::vector >& peers, uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent, bool isShort, - i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports): // outbound - m_IsShort (isShort), m_FarEndTransports (farEndTransports) - { - CreatePeers (peers); - m_FirstHop->isGateway = false; - m_LastHop->SetReplyHop (replyTunnelID, replyIdent); - } - + i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports); // outbound + virtual ~TunnelConfig () { TunnelHopConfig * hop = m_FirstHop; @@ -182,6 +189,9 @@ namespace tunnel } size_t GetRecordSize () const { return m_IsShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; }; + + void CreatePhonyHop (); + void DeletePhonyHop (); protected: