diff --git a/Destination.h b/Destination.h index e00f1b3e..97b62a5f 100644 --- a/Destination.h +++ b/Destination.h @@ -7,13 +7,14 @@ #include "TunnelPool.h" #include "CryptoConst.h" #include "NetDb.h" +#include "Garlic.h" #include "Streaming.h" namespace i2p { namespace stream { - class StreamingDestination: public i2p::data::LocalDestination + class StreamingDestination: public i2p::garlic::GarlicDestination { public: diff --git a/Garlic.cpp b/Garlic.cpp index 2c3e309e..a51a5aea 100644 --- a/Garlic.cpp +++ b/Garlic.cpp @@ -54,32 +54,131 @@ namespace garlic m_Decryption.SetIV (iv); m_Decryption.Decrypt (buf + 32, length - 32, buf + 32); } + HandleAESBlock (buf + 32, length - 32, msg->from); m_Tags.erase (it); // tag might be used only once } else { // tag not found. Use ElGamal - if (m_LocalDestination) - { - ElGamalBlock elGamal; - if (i2p::crypto::ElGamalDecrypt (m_LocalDestination->GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true)) - { - m_Decryption.SetKey (elGamal.sessionKey); - uint8_t iv[32]; // IV is first 16 bytes - CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); - m_Decryption.SetIV (iv); - m_Decryption.Decrypt(buf + 514, length - 514, buf + 514); - } - else - LogPrint ("Failed to decrypt garlic"); - } + ElGamalBlock elGamal; + if (i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true)) + { + m_Decryption.SetKey (elGamal.sessionKey); + uint8_t iv[32]; // IV is first 16 bytes + CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); + m_Decryption.SetIV (iv); + m_Decryption.Decrypt(buf + 514, length - 514, buf + 514); + HandleAESBlock (buf + 514, length - 514, msg->from); + } else - LogPrint ("Can't use ElGamal without local destination"); + LogPrint ("Failed to decrypt garlic"); } DeleteI2NPMessage (msg); } + void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from) + { + uint16_t tagCount = be16toh (*(uint16_t *)buf); + buf += 2; + if (tagCount > 0) + { + for (int i = 0; i < tagCount; i++) + m_Tags[SessionTag(buf + i*32)] = nullptr; + } + buf += tagCount*32; + uint32_t payloadSize = be32toh (*(uint32_t *)buf); + if (payloadSize > len) + { + LogPrint ("Unexpected payload size ", payloadSize); + return; + } + buf += 4; + uint8_t * payloadHash = buf; + buf += 32;// payload hash. + if (*buf) // session key? + buf += 32; // new session key + buf++; // flag + // payload + uint8_t hash[32]; + CryptoPP::SHA256().CalculateDigest(hash, buf, payloadSize); + if (memcmp (hash, payloadHash, 32)) // payload hash doesn't match + { + LogPrint ("Wrong payload hash"); + return; + } + HandleGarlicPayload (buf, payloadSize, from); + } + + void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from) + { + int numCloves = buf[0]; + LogPrint (numCloves," cloves"); + buf++; + for (int i = 0; i < numCloves; i++) + { + // delivery instructions + uint8_t flag = buf[0]; + buf++; // flag + if (flag & 0x80) // encrypted? + { + // TODO: implement + LogPrint ("Clove encrypted"); + buf += 32; + } + GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03); + switch (deliveryType) + { + case eGarlicDeliveryTypeLocal: + LogPrint ("Garlic type local"); + i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); + break; + case eGarlicDeliveryTypeDestination: + { + LogPrint ("Garlic type destination"); + buf += 32; // destination. check it later or for multiple destinations + I2NPHeader * header = (I2NPHeader *)buf; + if (header->typeID == eI2NPData) + HandleDataMessage (buf + sizeof (I2NPHeader), be16toh (header->size)); + else + LogPrint ("Unexpected I2NP garlic message ", (int)header->typeID); + break; + } + case eGarlicDeliveryTypeTunnel: + { + LogPrint ("Garlic type tunnel"); + // gwHash and gwTunnel sequence is reverted + uint8_t * gwHash = buf; + buf += 32; + uint32_t gwTunnel = be32toh (*(uint32_t *)buf); + buf += 4; + i2p::tunnel::OutboundTunnel * tunnel = nullptr; + if (from && from->GetTunnelPool ()) + tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel (); + if (tunnel) // we have send it through an outbound tunnel + { + I2NPMessage * msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from); + tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg); + } + else + LogPrint ("No outbound tunnels available for garlic clove"); + break; + } + case eGarlicDeliveryTypeRouter: + LogPrint ("Garlic type router not supported"); + buf += 32; + break; + default: + LogPrint ("Unknow garlic delivery type ", (int)deliveryType); + } + buf += GetI2NPMessageLength (buf); // I2NP + buf += 4; // CloveID + buf += 8; // Date + buf += 3; // Certificate + } + } + + GarlicRoutingSession::GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags): m_Destination (destination), m_IsAcknowledged (false), m_NumTags (numTags), m_NextTag (-1), m_SessionTags (0), m_TagsCreationTime (0), m_LocalLeaseSet (nullptr) @@ -347,17 +446,6 @@ namespace garlic for (auto it: m_Sessions) delete it.second; m_Sessions.clear (); - // TODO: delete remaining session decryptions - m_SessionTags.clear (); - } - - void GarlicRouting::AddSessionKey (const uint8_t * key, const uint8_t * tag) - { - SessionDecryption * decryption = new SessionDecryption; - decryption->SetKey (key); - decryption->SetTagCount (1); - std::unique_lock l(m_SessionsTagsMutex); - m_SessionTags[SessionTag(tag)] = decryption; } GarlicRoutingSession * GarlicRouting::GetRoutingSession ( @@ -396,155 +484,13 @@ namespace garlic void GarlicRouting::HandleGarlicMessage (I2NPMessage * msg) { - uint8_t * buf = msg->GetPayload (); - uint32_t length = be32toh (*(uint32_t *)buf); - buf += 4; - auto it = m_SessionTags.find (SessionTag(buf)); - if (it != m_SessionTags.end ()) - { - // existing session - uint8_t iv[32]; // IV is first 16 bytes - CryptoPP::SHA256().CalculateDigest(iv, buf, 32); - it->second->SetIV (iv); - it->second->Decrypt (buf + 32, length - 32, buf + 32); - it->second->UseTag (); - HandleAESBlock (buf + 32, length - 32, it->second, msg->from); - if (!it->second->GetTagCount ()) delete it->second; // all tags were used - std::unique_lock l(m_SessionsTagsMutex); - m_SessionTags.erase (it); // tag might be used only once - } + auto pool = msg->from ? msg->from->GetTunnelPool () : nullptr; + if (pool) + pool->GetGarlicDestination ().HandleGarlicMessage (msg); else { - // new session - i2p::tunnel::TunnelPool * pool = nullptr; - if (msg->from) - pool = msg->from->GetTunnelPool (); - ElGamalBlock elGamal; - if (i2p::crypto::ElGamalDecrypt ( - pool ? pool->GetEncryptionPrivateKey () : i2p::context.GetPrivateKey (), - buf, (uint8_t *)&elGamal, true)) - { - SessionDecryption * decryption = new SessionDecryption; - decryption->SetKey (elGamal.sessionKey); - uint8_t iv[32]; // IV is first 16 bytes - CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); - decryption->SetIV (iv); - decryption->Decrypt(buf + 514, length - 514, buf + 514); - HandleAESBlock (buf + 514, length - 514, decryption, msg->from); - } - else - LogPrint ("Failed to decrypt garlic"); - } - DeleteI2NPMessage (msg); - } - - void GarlicRouting::HandleAESBlock (uint8_t * buf, size_t len, SessionDecryption * decryption, i2p::tunnel::InboundTunnel * from) - { - uint16_t tagCount = be16toh (*(uint16_t *)buf); - buf += 2; - if (tagCount > 0) - { - decryption->AddTagCount (tagCount); - std::unique_lock l(m_SessionsTagsMutex); - for (int i = 0; i < tagCount; i++) - m_SessionTags[SessionTag(buf + i*32)] = decryption; - } - buf += tagCount*32; - uint32_t payloadSize = be32toh (*(uint32_t *)buf); - if (payloadSize > len) - { - LogPrint ("Unexpected payload size ", payloadSize); - return; - } - buf += 4; - uint8_t * payloadHash = buf; - buf += 32;// payload hash. - if (*buf) // session key? - buf += 32; // new session key - buf++; // flag - - // payload - uint8_t hash[32]; - CryptoPP::SHA256().CalculateDigest(hash, buf, payloadSize); - if (memcmp (hash, payloadHash, 32)) // payload hash doesn't match - { - LogPrint ("Wrong payload hash"); - return; - } - HandleGarlicPayload (buf, payloadSize, from); - } - - void GarlicRouting::HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from) - { - int numCloves = buf[0]; - LogPrint (numCloves," cloves"); - buf++; - for (int i = 0; i < numCloves; i++) - { - // delivery instructions - uint8_t flag = buf[0]; - buf++; // flag - if (flag & 0x80) // encrypted? - { - // TODO: implement - LogPrint ("Clove encrypted"); - buf += 32; - } - GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03); - switch (deliveryType) - { - case eGarlicDeliveryTypeLocal: - LogPrint ("Garlic type local"); - i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); - break; - case eGarlicDeliveryTypeDestination: - { - LogPrint ("Garlic type destination"); - buf += 32; // destination. check it later or for multiple destinations - I2NPHeader * header = (I2NPHeader *)buf; - if (header->typeID == eI2NPData) - { - auto pool = from ? from->GetTunnelPool () : nullptr; - if (pool) - pool->GetLocalDestination ().HandleDataMessage (buf + sizeof (I2NPHeader), be16toh (header->size)); - else - LogPrint ("Local destination doesn't exist"); - } - else - LogPrint ("Unexpected I2NP garlic message ", (int)header->typeID); - break; - } - case eGarlicDeliveryTypeTunnel: - { - LogPrint ("Garlic type tunnel"); - // gwHash and gwTunnel sequence is reverted - uint8_t * gwHash = buf; - buf += 32; - uint32_t gwTunnel = be32toh (*(uint32_t *)buf); - buf += 4; - i2p::tunnel::OutboundTunnel * tunnel = nullptr; - if (from && from->GetTunnelPool ()) - tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel (); - if (tunnel) // we have send it through an outbound tunnel - { - I2NPMessage * msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from); - tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg); - } - else - LogPrint ("No outbound tunnels available for garlic clove"); - break; - } - case eGarlicDeliveryTypeRouter: - LogPrint ("Garlic type router not supported"); - buf += 32; - break; - default: - LogPrint ("Unknow garlic delivery type ", (int)deliveryType); - } - buf += GetI2NPMessageLength (buf); // I2NP - buf += 4; // CloveID - buf += 8; // Date - buf += 3; // Certificate + LogPrint ("Local destination doesn't exist"); + DeleteI2NPMessage (msg); } } diff --git a/Garlic.h b/Garlic.h index 256e9d2b..95b8aeef 100644 --- a/Garlic.h +++ b/Garlic.h @@ -11,7 +11,6 @@ #include "aes.h" #include "I2NPProtocol.h" #include "LeaseSet.h" -#include "Tunnel.h" #include "Queue.h" #include "Identity.h" @@ -40,22 +39,25 @@ namespace garlic const int TAGS_EXPIRATION_TIMEOUT = 900; // 15 minutes typedef i2p::data::Tag<32> SessionTag; - class GarlicDestination + class GarlicDestination: public i2p::data::LocalDestination { public: - GarlicDestination (i2p::data::LocalDestination * localDestination = nullptr): - m_LocalDestination (localDestination) {}; + GarlicDestination () {}; ~GarlicDestination (); void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag void HandleGarlicMessage (I2NPMessage * msg); + private: + + void HandleAESBlock (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from); + void HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from); + private: i2p::crypto::CBCDecryption m_Decryption; std::map m_Tags; // tag->key, if null use key from decryption - i2p::data::LocalDestination * m_LocalDestination; }; class GarlicRoutingSession @@ -95,22 +97,7 @@ namespace garlic }; class GarlicRouting - { - class SessionDecryption: public i2p::crypto::CBCDecryption - { - public: - - SessionDecryption (): m_TagCount (0) {}; - void SetTagCount (int tagCount) { m_TagCount = tagCount; }; - void AddTagCount (int tagCount) { m_TagCount += tagCount; }; - int GetTagCount () const { return m_TagCount; }; - bool UseTag () { m_TagCount--; return m_TagCount > 0; }; - - private: - - int m_TagCount; - }; - + { public: GarlicRouting (); @@ -119,7 +106,6 @@ namespace garlic void Start (); void Stop (); void PostI2NPMsg (I2NPMessage * msg); - void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag GarlicRoutingSession * GetRoutingSession (const i2p::data::RoutingDestination& destination, int numTags); I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg); @@ -133,9 +119,7 @@ namespace garlic void Run (); void HandleGarlicMessage (I2NPMessage * msg); void HandleDeliveryStatusMessage (I2NPMessage * msg); - void HandleAESBlock (uint8_t * buf, size_t len, SessionDecryption * decryption, i2p::tunnel::InboundTunnel * from); - void HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from); - + private: bool m_IsRunning; @@ -146,10 +130,6 @@ namespace garlic std::map m_Sessions; std::mutex m_CreatedSessionsMutex; std::map m_CreatedSessions; // msgID -> session - // incoming session - // multiple tags refer to one decyption - std::mutex m_SessionsTagsMutex; - std::map m_SessionTags; // tag -> decryption }; extern GarlicRouting routing; diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp index 43167687..98941df3 100644 --- a/I2NPProtocol.cpp +++ b/I2NPProtocol.cpp @@ -103,7 +103,7 @@ namespace i2p I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, uint32_t replyTunnelID, bool exploratory, std::set * excludedPeers, - bool encryption) + bool encryption, i2p::tunnel::TunnelPool * pool) { I2NPMessage * m = NewI2NPMessage (); uint8_t * buf = m->GetPayload (); @@ -159,7 +159,10 @@ namespace i2p rnd.GenerateBlock (buf, 32); // key buf[32] = 1; // 1 tag rnd.GenerateBlock (buf + 33, 32); // tag - i2p::garlic::routing.AddSessionKey (buf, buf + 33); // introduce new key-tag to garlic engine + if (pool) + pool->GetGarlicDestination ().AddSessionKey (buf, buf + 33); // introduce new key-tag to garlic engine + else + LogPrint ("Destination for encrypteed reply not specified"); buf += 65; } m->len += (buf - m->GetPayload ()); diff --git a/I2NPProtocol.h b/I2NPProtocol.h index 7f62bc5d..1493c26f 100644 --- a/I2NPProtocol.h +++ b/I2NPProtocol.h @@ -99,6 +99,7 @@ namespace i2p namespace tunnel { class InboundTunnel; + class TunnelPool; } const size_t I2NP_MAX_MESSAGE_SIZE = 32768; @@ -168,7 +169,8 @@ namespace tunnel I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID); I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, uint32_t replyTunnelID, bool exploratory = false, - std::set * excludedPeers = nullptr, bool encryption = false); + std::set * excludedPeers = nullptr, bool encryption = false, + i2p::tunnel::TunnelPool * pool = nullptr); I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, const i2p::data::RouterInfo * floodfill); I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router = nullptr); diff --git a/NetDb.cpp b/NetDb.cpp index 4aa48be4..55c4e549 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -23,7 +23,8 @@ namespace data const i2p::tunnel::InboundTunnel * replyTunnel) { I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination, - replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, &m_ExcludedPeers, m_IsLeaseSet); + replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, + &m_ExcludedPeers, m_IsLeaseSet, m_Pool); if (m_IsLeaseSet) // wrap lookup message into garlic msg = i2p::garlic::routing.WrapSingleMessage (*router, msg); m_ExcludedPeers.insert (router->GetIdentHash ()); @@ -102,7 +103,7 @@ namespace data { try { - I2NPMessage * msg = m_Queue.GetNextWithTimeout (30000); // 30 sec + I2NPMessage * msg = m_Queue.GetNextWithTimeout (15000); // 15 sec if (msg) { while (msg) diff --git a/RouterContext.h b/RouterContext.h index ffb4a1e5..2251deff 100644 --- a/RouterContext.h +++ b/RouterContext.h @@ -6,6 +6,7 @@ #include #include "Identity.h" #include "RouterInfo.h" +#include "Garlic.h" namespace i2p { @@ -13,7 +14,7 @@ namespace i2p const char ROUTER_KEYS[] = "router.keys"; const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes - class RouterContext: public i2p::data::LocalDestination + class RouterContext: public i2p::garlic::GarlicDestination { public: diff --git a/Streaming.h b/Streaming.h index 66f16282..ae958d4f 100644 --- a/Streaming.h +++ b/Streaming.h @@ -14,6 +14,7 @@ #include "LeaseSet.h" #include "I2NPProtocol.h" #include "Garlic.h" +#include "Tunnel.h" namespace i2p { diff --git a/Tunnel.cpp b/Tunnel.cpp index 57887709..85abd5a8 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -288,7 +288,7 @@ namespace tunnel return tunnel; } - TunnelPool * Tunnels::CreateTunnelPool (i2p::data::LocalDestination& localDestination, int numHops) + TunnelPool * Tunnels::CreateTunnelPool (i2p::garlic::GarlicDestination& localDestination, int numHops) { auto pool = new TunnelPool (localDestination, numHops); std::unique_lock l(m_PoolsMutex); diff --git a/Tunnel.h b/Tunnel.h index 81ffebda..12eec8ea 100644 --- a/Tunnel.h +++ b/Tunnel.h @@ -129,7 +129,7 @@ namespace tunnel void PostTunnelData (I2NPMessage * msg); template TTunnel * CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel = 0); - TunnelPool * CreateTunnelPool (i2p::data::LocalDestination& localDestination, int numHops); + TunnelPool * CreateTunnelPool (i2p::garlic::GarlicDestination& localDestination, int numHops); void DeleteTunnelPool (TunnelPool * pool); private: diff --git a/TunnelPool.cpp b/TunnelPool.cpp index b6578323..37091206 100644 --- a/TunnelPool.cpp +++ b/TunnelPool.cpp @@ -10,7 +10,7 @@ namespace i2p { namespace tunnel { - TunnelPool::TunnelPool (i2p::data::LocalDestination& localDestination, int numHops, int numTunnels): + TunnelPool::TunnelPool (i2p::garlic::GarlicDestination& localDestination, int numHops, int numTunnels): m_LocalDestination (localDestination), m_NumHops (numHops), m_NumTunnels (numTunnels) { } diff --git a/TunnelPool.h b/TunnelPool.h index c4a09d1c..1a231a94 100644 --- a/TunnelPool.h +++ b/TunnelPool.h @@ -12,6 +12,7 @@ #include "I2NPProtocol.h" #include "TunnelBase.h" #include "RouterContext.h" +#include "Garlic.h" namespace i2p { @@ -25,13 +26,13 @@ namespace tunnel { public: - TunnelPool (i2p::data::LocalDestination& localDestination, int numHops, int numTunnels = 5); + TunnelPool (i2p::garlic::GarlicDestination& localDestination, int numHops, int numTunnels = 5); ~TunnelPool (); const uint8_t * GetEncryptionPrivateKey () const { return m_LocalDestination.GetEncryptionPrivateKey (); }; const uint8_t * GetEncryptionPublicKey () const { return m_LocalDestination.GetEncryptionPublicKey (); }; const i2p::data::LocalDestination& GetLocalDestination () const { return m_LocalDestination; }; - i2p::data::LocalDestination& GetLocalDestination () { return m_LocalDestination; }; + i2p::garlic::GarlicDestination& GetGarlicDestination () const { return m_LocalDestination; }; bool IsExploratory () const { return GetIdentHash () == i2p::context.GetRouterIdentHash (); }; void CreateTunnels (); @@ -60,7 +61,7 @@ namespace tunnel private: - i2p::data::LocalDestination& m_LocalDestination; + i2p::garlic::GarlicDestination& m_LocalDestination; int m_NumHops, m_NumTunnels; mutable std::mutex m_InboundTunnelsMutex; std::set m_InboundTunnels; // recent tunnel appears first