fill phony record for inbound tunnel with x25519 public key

This commit is contained in:
orignal 2025-05-25 14:49:58 -04:00
parent 4c6be3b85a
commit 606881898b
4 changed files with 110 additions and 26 deletions

View file

@ -29,7 +29,7 @@ namespace i2p
{
namespace tunnel
{
Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config):
Tunnel::Tunnel (std::shared_ptr<TunnelConfig> 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> 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<int> 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]()

View file

@ -81,12 +81,12 @@ namespace tunnel
/** function for visiting a hops stored in a tunnel */
typedef std::function<void(std::shared_ptr<const i2p::data::IdentityEx>)> TunnelHopVisitor;
Tunnel (std::shared_ptr<const TunnelConfig> config);
Tunnel (std::shared_ptr<TunnelConfig> config);
~Tunnel ();
void Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
std::shared_ptr<const TunnelConfig> GetTunnelConfig () const { return m_Config; }
std::shared_ptr<TunnelConfig> GetTunnelConfig () const { return m_Config; }
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const;
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetInvertedPeers () const;
bool IsShortBuildMessage () const { return m_IsShortBuildMessage; };
@ -125,7 +125,7 @@ namespace tunnel
private:
std::shared_ptr<const TunnelConfig> m_Config;
std::shared_ptr<TunnelConfig> m_Config;
std::vector<TunnelHop> m_Hops;
bool m_IsShortBuildMessage;
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
@ -139,7 +139,7 @@ namespace tunnel
{
public:
OutboundTunnel (std::shared_ptr<const TunnelConfig> config):
OutboundTunnel (std::shared_ptr<TunnelConfig> config):
Tunnel (config), m_Gateway (*this), m_EndpointIdentHash (config->GetLastIdentHash ()) {};
void SendTunnelDataMsgTo (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg);
@ -164,7 +164,7 @@ namespace tunnel
{
public:
InboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Endpoint (true) {};
InboundTunnel (std::shared_ptr<TunnelConfig> config): Tunnel (config), m_Endpoint (true) {};
void HandleTunnelDataMsg (std::shared_ptr<I2NPMessage>&& msg) override;
virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
bool IsInbound() const override { return true; }

View file

@ -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<std::shared_ptr<const i2p::data::IdentityEx> >& 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<std::shared_ptr<const i2p::data::IdentityEx> >& 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<std::shared_ptr<const i2p::data::IdentityEx> >& 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;
}
}
}
}

View file

@ -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<std::shared_ptr<const i2p::data::IdentityEx> >& 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<std::shared_ptr<const i2p::data::IdentityEx> >& 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: