From 49d67bada03af481880b5cde22320de6015034fb Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 7 Oct 2014 16:18:13 -0400 Subject: [PATCH] AES/ElGamal session tags per local destination --- Destination.cpp | 27 ++-- Destination.h | 4 - Garlic.cpp | 327 +++++++++++++++++++++++------------------------- Garlic.h | 67 +++++----- NetDb.cpp | 9 +- Streaming.cpp | 2 +- 6 files changed, 215 insertions(+), 221 deletions(-) diff --git a/Destination.cpp b/Destination.cpp index 74c8d410..aee69c2b 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "Log.h" #include "util.h" #include "Destination.h" @@ -177,22 +178,22 @@ namespace stream if (buf[9] == 6) // streaming protocol { // unzip it - CryptoPP::Gunzip m_Decompressor; - m_Decompressor.Put (buf, length); - m_Decompressor.MessageEnd(); + CryptoPP::Gunzip decompressor; + decompressor.Put (buf, length); + decompressor.MessageEnd(); Packet * uncompressed = new Packet; uncompressed->offset = 0; - uncompressed->len = m_Decompressor.MaxRetrievable (); + uncompressed->len = decompressor.MaxRetrievable (); if (uncompressed->len <= MAX_PACKET_SIZE) { - m_Decompressor.Get (uncompressed->buf, uncompressed->len); + decompressor.Get (uncompressed->buf, uncompressed->len); // then forward to streaming thread m_Service.post (boost::bind (&StreamingDestination::HandleNextPacket, this, uncompressed)); } else { LogPrint ("Received packet size ", uncompressed->len, " exceeds max packet size. Skipped"); - m_Decompressor.Skip (); + decompressor.Skip (); delete uncompressed; } } @@ -203,18 +204,18 @@ namespace stream I2NPMessage * StreamingDestination::CreateDataMessage (const uint8_t * payload, size_t len) { I2NPMessage * msg = NewI2NPShortMessage (); - CryptoPP::Gzip m_Compressor; + CryptoPP::Gzip compressor; if (len <= COMPRESSION_THRESHOLD_SIZE) - m_Compressor.SetDeflateLevel (CryptoPP::Gzip::MIN_DEFLATE_LEVEL); + compressor.SetDeflateLevel (CryptoPP::Gzip::MIN_DEFLATE_LEVEL); else - m_Compressor.SetDeflateLevel (CryptoPP::Gzip::DEFAULT_DEFLATE_LEVEL); - m_Compressor.Put (payload, len); - m_Compressor.MessageEnd(); - int size = m_Compressor.MaxRetrievable (); + compressor.SetDeflateLevel (CryptoPP::Gzip::DEFAULT_DEFLATE_LEVEL); + compressor.Put (payload, len); + compressor.MessageEnd(); + int size = compressor.MaxRetrievable (); uint8_t * buf = msg->GetPayload (); *(uint32_t *)buf = htobe32 (size); // length buf += 4; - m_Compressor.Get (buf, size); + compressor.Get (buf, size); memset (buf + 4, 0, 4); // source and destination ports. TODO: fill with proper values later buf[9] = 6; // streaming protocol msg->len += size + 4; diff --git a/Destination.h b/Destination.h index 04efff9b..c9f36d5d 100644 --- a/Destination.h +++ b/Destination.h @@ -3,7 +3,6 @@ #include #include -#include #include "Identity.h" #include "TunnelPool.h" #include "CryptoConst.h" @@ -64,9 +63,6 @@ namespace stream bool m_IsPublic; std::function m_Acceptor; - - //CryptoPP::Gzip m_Compressor; - //CryptoPP::Gunzip m_Decompressor; }; class StreamingDestinations diff --git a/Garlic.cpp b/Garlic.cpp index 0c335ada..b4d5928b 100644 --- a/Garlic.cpp +++ b/Garlic.cpp @@ -14,156 +14,6 @@ namespace i2p { namespace garlic { - void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag) - { - if (key) - { - auto decryption = std::make_shared(); - decryption->SetKey (key); - m_Tags[SessionTag(tag)] = decryption; - } - } - - void GarlicDestination::HandleGarlicMessage (I2NPMessage * msg) - { - uint8_t * buf = msg->GetPayload (); - uint32_t length = be32toh (*(uint32_t *)buf); - buf += 4; // lentgh - auto it = m_Tags.find (SessionTag(buf)); - if (it != m_Tags.end ()) - { - // tag found. Use AES - 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); - HandleAESBlock (buf + 32, length - 32, it->second, msg->from); - m_Tags.erase (it); // tag might be used only once - } - else - { - // tag not found. Use ElGamal - ElGamalBlock elGamal; - if (i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true)) - { - auto decryption = std::make_shared(); - 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 GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr decryption, - 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)] = 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 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) @@ -420,20 +270,171 @@ namespace garlic return size; } - - GarlicRouting routing; - GarlicRouting::GarlicRouting (): m_IsRunning (false), m_Thread (nullptr) - { - } - GarlicRouting::~GarlicRouting () + GarlicDestination::~GarlicDestination () { for (auto it: m_Sessions) delete it.second; m_Sessions.clear (); + } + + void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag) + { + if (key) + { + auto decryption = std::make_shared(); + decryption->SetKey (key); + m_Tags[SessionTag(tag)] = decryption; + } + } + + void GarlicDestination::HandleGarlicMessage (I2NPMessage * msg) + { + uint8_t * buf = msg->GetPayload (); + uint32_t length = be32toh (*(uint32_t *)buf); + buf += 4; // lentgh + auto it = m_Tags.find (SessionTag(buf)); + if (it != m_Tags.end ()) + { + // tag found. Use AES + 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); + HandleAESBlock (buf + 32, length - 32, it->second, msg->from); + m_Tags.erase (it); // tag might be used only once + } + else + { + // tag not found. Use ElGamal + ElGamalBlock elGamal; + if (i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true)) + { + auto decryption = std::make_shared(); + 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); } - GarlicRoutingSession * GarlicRouting::GetRoutingSession ( + void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr decryption, + 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)] = 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 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 + } + } + + I2NPMessage * GarlicDestination::WrapMessage (const i2p::data::RoutingDestination& destination, + I2NPMessage * msg, const i2p::data::LeaseSet * leaseSet) + { + auto session = GetRoutingSession (destination, leaseSet ? 32 : 0); // don't use tag if no LeaseSet + return session->WrapSingleMessage (msg, leaseSet); + } + + GarlicRoutingSession * GarlicDestination::GetRoutingSession ( const i2p::data::RoutingDestination& destination, int numTags) { auto it = m_Sessions.find (destination.GetIdentHash ()); @@ -448,18 +449,8 @@ namespace garlic } return session; } - - I2NPMessage * GarlicRouting::WrapSingleMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg) - { - return WrapMessage (destination, msg, nullptr); - } - I2NPMessage * GarlicRouting::WrapMessage (const i2p::data::RoutingDestination& destination, - I2NPMessage * msg, const i2p::data::LeaseSet * leaseSet) - { - auto session = GetRoutingSession (destination, leaseSet ? 32 : 0); // don't use tag if no LeaseSet - return session->WrapSingleMessage (msg, leaseSet); - } + GarlicRouting routing; void GarlicRouting::DeliveryStatusSent (GarlicRoutingSession * session, uint32_t msgID) { diff --git a/Garlic.h b/Garlic.h index 1433e04d..d41e3804 100644 --- a/Garlic.h +++ b/Garlic.h @@ -37,30 +37,9 @@ namespace garlic }; #pragma pack() - const int TAGS_EXPIRATION_TIMEOUT = 900; // 15 minutes - - typedef i2p::data::Tag<32> SessionTag; - class GarlicDestination: public i2p::data::LocalDestination - { - public: - - 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, std::shared_ptr decryption, - i2p::tunnel::InboundTunnel * from); - void HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from); - - private: - - std::map> m_Tags; - }; + const int TAGS_EXPIRATION_TIMEOUT = 900; // 15 minutes + typedef i2p::data::Tag<32> SessionTag; class GarlicRoutingSession { public: @@ -97,22 +76,46 @@ namespace garlic CryptoPP::AutoSeededRandomPool m_Rnd; }; + class GarlicDestination: public i2p::data::LocalDestination + { + public: + + GarlicDestination () {}; + ~GarlicDestination (); + + GarlicRoutingSession * GetRoutingSession (const i2p::data::RoutingDestination& destination, int numTags); + I2NPMessage * WrapMessage (const i2p::data::RoutingDestination& destination, + I2NPMessage * msg, const i2p::data::LeaseSet * leaseSet = nullptr); + + 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, std::shared_ptr decryption, + i2p::tunnel::InboundTunnel * from); + void HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from); + + private: + + // outgoing sessions + std::mutex m_SessionsMutex; + std::map m_Sessions; + // incoming + std::map> m_Tags; + }; + class GarlicRouting { public: - GarlicRouting (); - ~GarlicRouting (); + GarlicRouting (): m_IsRunning (false), m_Thread (nullptr) {}; + ~GarlicRouting () {}; void Start (); void Stop (); void PostI2NPMsg (I2NPMessage * msg); - GarlicRoutingSession * GetRoutingSession (const i2p::data::RoutingDestination& destination, int numTags); - I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg); - I2NPMessage * WrapMessage (const i2p::data::RoutingDestination& destination, - I2NPMessage * msg, const i2p::data::LeaseSet * leaseSet = nullptr); - void DeliveryStatusSent (GarlicRoutingSession * session, uint32_t msgID); private: @@ -126,9 +129,7 @@ namespace garlic bool m_IsRunning; std::thread * m_Thread; i2p::util::Queue m_Queue; - // outgoing sessions - std::mutex m_SessionsMutex; - std::map m_Sessions; + std::mutex m_CreatedSessionsMutex; std::map m_CreatedSessions; // msgID -> session }; diff --git a/NetDb.cpp b/NetDb.cpp index 55c4e549..62291db7 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -26,7 +26,12 @@ namespace data 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); + { + if (m_Pool) + msg = m_Pool->GetGarlicDestination ().WrapMessage (*router, msg); + else + LogPrint ("Can't create garlic message without destination"); + } m_ExcludedPeers.insert (router->GetIdentHash ()); m_LastRouter = router; m_CreationTime = i2p::util::GetSecondsSinceEpoch (); @@ -943,7 +948,7 @@ namespace data return; } uint32_t replyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); - auto msg = i2p::garlic::routing.WrapSingleMessage (*floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, replyToken)); + auto msg = pool->GetGarlicDestination ().WrapMessage (*floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, replyToken)); outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg); } } diff --git a/Streaming.cpp b/Streaming.cpp index 6e3bf260..c1670039 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -488,7 +488,7 @@ namespace stream if (m_RemoteLeaseSet) { if (!m_RoutingSession) - m_RoutingSession = i2p::garlic::routing.GetRoutingSession (*m_RemoteLeaseSet, 32); + m_RoutingSession = m_LocalDestination.GetRoutingSession (*m_RemoteLeaseSet, 32); auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (); if (!leases.empty ()) {