always send short tunnel build message

This commit is contained in:
orignal
2026-06-10 21:08:41 -04:00
parent 6cfef96863
commit f50cf4df87
6 changed files with 39 additions and 145 deletions
+8 -13
View File
@@ -31,7 +31,7 @@ namespace tunnel
{
Tunnel::Tunnel (std::shared_ptr<TunnelConfig> config):
TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()),
m_Config (config), m_IsShortBuildMessage (false), m_Pool (nullptr),
m_Config (config), m_Pool (nullptr),
m_State (eTunnelStatePending), m_FarEndTransports (i2p::data::RouterInfo::eAllTransports),
m_IsRecreated (false), m_Latency (UNKNOWN_LATENCY)
{
@@ -53,7 +53,7 @@ namespace tunnel
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;
const size_t recordSize = SHORT_TUNNEL_BUILD_RECORD_SIZE;
msg->len += numRecords*recordSize + 1;
// shuffle records
std::vector<int> recordIndicies;
@@ -97,7 +97,7 @@ namespace tunnel
// delete phony hop after encryption
if (insertPhonyRecord) m_Config->DeletePhonyHop ();
msg->FillI2NPMessageHeader (m_Config->IsShort () ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild);
msg->FillI2NPMessageHeader (eI2NPShortTunnelBuild);
auto s = shared_from_this ();
msg->onDrop = [s]()
{
@@ -108,21 +108,17 @@ namespace tunnel
// send message
if (outboundTunnel)
{
if (m_Config->IsShort ())
auto ident = m_Config->GetFirstHop () ? m_Config->GetFirstHop ()->ident : nullptr;
if (ident && ident->GetIdentHash () != outboundTunnel->GetEndpointIdentHash ()) // don't encrypt if IBGW = OBEP
{
auto ident = m_Config->GetFirstHop () ? m_Config->GetFirstHop ()->ident : nullptr;
if (ident && ident->GetIdentHash () != outboundTunnel->GetEndpointIdentHash ()) // don't encrypt if IBGW = OBEP
{
auto msg1 = i2p::garlic::WrapECIESX25519MessageForRouter (msg, ident->GetEncryptionPublicKey ());
if (msg1) msg = msg1;
}
auto msg1 = i2p::garlic::WrapECIESX25519MessageForRouter (msg, ident->GetEncryptionPublicKey ());
if (msg1) msg = msg1;
}
outboundTunnel->SendTunnelDataMsgTo (GetNextIdentHash (), 0, msg);
}
else
{
if (m_Config->IsShort () && m_Config->GetLastHop () &&
m_Config->GetLastHop ()->ident->GetIdentHash () != m_Config->GetLastHop ()->nextIdent)
if (m_Config->GetLastHop () && m_Config->GetLastHop ()->ident->GetIdentHash () != m_Config->GetLastHop ()->nextIdent)
{
// add garlic key/tag for reply
uint8_t key[32];
@@ -212,7 +208,6 @@ namespace tunnel
hop = hop->prev;
i++;
}
m_IsShortBuildMessage = m_Config->IsShort ();
m_FarEndTransports = m_Config->GetFarEndTransports ();
m_Config = nullptr;
}
-2
View File
@@ -89,7 +89,6 @@ namespace tunnel
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; };
i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const { return m_FarEndTransports; };
TunnelState GetState () const { return m_State; };
void SetState (TunnelState state);
@@ -127,7 +126,6 @@ namespace tunnel
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
std::atomic<TunnelState> m_State;
i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports;
+13 -84
View File
@@ -109,50 +109,6 @@ namespace tunnel
return i2p::crypto::AEADChaCha20Poly1305 (encrypted, len - 16, m_H, 32, key, nonce, clearText, len - 16, false); // decrypt
}
void LongECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID)
{
// generate keys
RAND_bytes (layerKey, 32);
RAND_bytes (ivKey, 32);
RAND_bytes (replyKey, 32);
RAND_bytes (replyIV, 16);
// fill clear text
uint8_t flag = 0;
if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG;
if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
memset (clearText + ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 3); // set to 0 for compatibility
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ());
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes
htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
memset (clearText + ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET, 0, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET);
// encrypt
uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE;
EncryptECIES (clearText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
}
bool LongECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const
{
uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE;
uint8_t nonce[12];
memset (nonce, 0, 12);
if (!DecryptECIES (m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE, record))
{
LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
return false;
}
return true;
}
void ShortECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID)
{
// fill clear text
@@ -225,14 +181,6 @@ 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;
@@ -242,8 +190,8 @@ namespace tunnel
}
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)
i2p::data::RouterInfo::CompatibleTransports farEndTransports):
m_FarEndTransports (farEndTransports)
{
// inbound
CreatePeers (peers);
@@ -251,9 +199,9 @@ namespace tunnel
}
TunnelConfig::TunnelConfig (const std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers,
uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent, bool isShort,
uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent,
i2p::data::RouterInfo::CompatibleTransports farEndTransports):
m_IsShort (isShort), m_FarEndTransports (farEndTransports)
m_FarEndTransports (farEndTransports)
{
// outbound
CreatePeers (peers);
@@ -266,24 +214,12 @@ namespace tunnel
TunnelHopConfig * prev = nullptr;
for (const auto& it: peers)
{
TunnelHopConfig * hop = nullptr;
if (m_IsShort)
hop = new ShortECIESTunnelHopConfig (it);
TunnelHopConfig * hop = new ShortECIESTunnelHopConfig (it);
if (prev)
prev->SetNext (hop);
else
{
if (it->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
hop = new LongECIESTunnelHopConfig (it);
else
LogPrint (eLogError, "Tunnel: ElGamal router is not supported");
}
if (hop)
{
if (prev)
prev->SetNext (hop);
else
m_FirstHop = hop;
prev = hop;
}
m_FirstHop = hop;
prev = hop;
}
m_LastHop = prev;
}
@@ -292,17 +228,10 @@ namespace tunnel
{
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;
}
TunnelHopConfig * hop = new ShortPhonyTunnelHopConfig ();
hop->prev = m_LastHop;
m_LastHop->next = hop;
m_LastHop = hop;
}
}
+8 -26
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2025, The PurpleI2P Project
* Copyright (c) 2013-2026, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -55,16 +55,6 @@ namespace tunnel
bool DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const;
};
struct LongECIESTunnelHopConfig: public ECIESTunnelHopConfig
{
LongECIESTunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r):
ECIESTunnelHopConfig (r) {};
uint8_t GetRetCode (const uint8_t * records) const override
{ return (records + recordIndex*TUNNEL_BUILD_RECORD_SIZE)[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET]; };
void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override;
bool DecryptBuildResponseRecord (uint8_t * records) const override;
};
struct ShortECIESTunnelHopConfig: public ECIESTunnelHopConfig
{
ShortECIESTunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r):
@@ -85,27 +75,22 @@ namespace tunnel
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
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,
uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent,
i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports); // outbound
virtual ~TunnelConfig ()
{
TunnelHopConfig * hop = m_FirstHop;
@@ -118,8 +103,6 @@ namespace tunnel
}
}
bool IsShort () const { return m_IsShort; }
i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const
{
return m_FarEndTransports;
@@ -188,15 +171,15 @@ namespace tunnel
return peers;
}
size_t GetRecordSize () const { return m_IsShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; };
size_t GetRecordSize () const { return SHORT_TUNNEL_BUILD_RECORD_SIZE; };
void CreatePhonyHop ();
void DeletePhonyHop ();
protected:
// this constructor can't be called from outside
TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr), m_IsShort (false),
TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr),
m_FarEndTransports (i2p::data::RouterInfo::eAllTransports)
{
}
@@ -208,7 +191,6 @@ namespace tunnel
private:
TunnelHopConfig * m_FirstHop, * m_LastHop;
bool m_IsShort;
i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports;
};
+10 -19
View File
@@ -27,12 +27,7 @@ namespace tunnel
void Path::Add (std::shared_ptr<const i2p::data::RouterInfo> r)
{
if (r)
{
peers.push_back (r->GetRouterIdentity ());
if (r->GetVersion () < i2p::data::NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION ||
r->GetRouterIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
isShort = false;
}
}
void Path::Reverse ()
@@ -751,7 +746,7 @@ namespace tunnel
if (m_NumInboundHops > 0)
{
path.Reverse ();
config = std::make_shared<TunnelConfig> (path.peers, path.isShort, path.farEndTransports);
config = std::make_shared<TunnelConfig> (path.peers, path.farEndTransports);
}
auto tunnel = tunnels.CreateInboundTunnel (config, shared_from_this (), outboundTunnel);
if (tunnel->IsEstablished ()) // zero hops
@@ -777,8 +772,7 @@ namespace tunnel
{
auto peers = tunnel->GetPeers();
if (peers.size ()&& ValidatePeers (peers))
config = std::make_shared<TunnelConfig>(tunnel->GetPeers (),
tunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ());
config = std::make_shared<TunnelConfig>(tunnel->GetPeers (), tunnel->GetFarEndTransports ());
}
if (!m_NumInboundHops || config)
{
@@ -811,19 +805,17 @@ namespace tunnel
std::shared_ptr<TunnelConfig> config;
if (m_NumOutboundHops > 0)
config = std::make_shared<TunnelConfig>(path.peers, inboundTunnel->GetNextTunnelID (),
inboundTunnel->GetNextIdentHash (), path.isShort, path.farEndTransports);
inboundTunnel->GetNextIdentHash (), path.farEndTransports);
std::shared_ptr<OutboundTunnel> tunnel;
if (path.isShort)
// TODO: implement it better
tunnel = tunnels.CreateOutboundTunnel (config, inboundTunnel->GetTunnelPool ());
if (tunnel)
{
// TODO: implement it better
tunnel = tunnels.CreateOutboundTunnel (config, inboundTunnel->GetTunnelPool ());
tunnel->SetTunnelPool (shared_from_this ());
if (tunnel->IsEstablished ()) // zero hops
TunnelCreated (tunnel);
}
else
tunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ());
if (tunnel && tunnel->IsEstablished ()) // zero hops
TunnelCreated (tunnel);
}
else
LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available");
@@ -848,7 +840,7 @@ namespace tunnel
auto peers = tunnel->GetPeers();
if (peers.size () && ValidatePeers (peers))
config = std::make_shared<TunnelConfig>(peers, inboundTunnel->GetNextTunnelID (),
inboundTunnel->GetNextIdentHash (), inboundTunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ());
inboundTunnel->GetNextIdentHash (), tunnel->GetFarEndTransports ());
}
if (!m_NumOutboundHops || config)
{
@@ -865,8 +857,7 @@ namespace tunnel
{
LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
auto tunnel = tunnels.CreateInboundTunnel (
m_NumOutboundHops > 0 ? std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers (),
outboundTunnel->IsShortBuildMessage ()) : nullptr,
m_NumOutboundHops > 0 ? std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()) : nullptr,
shared_from_this (), outboundTunnel);
if (tunnel->IsEstablished ()) // zero hops
TunnelCreated (tunnel);
-1
View File
@@ -44,7 +44,6 @@ namespace tunnel
struct Path
{
std::vector<Peer> peers;
bool isShort = true;
i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports;
void Add (std::shared_ptr<const i2p::data::RouterInfo> r);