diff --git a/Garlic.cpp b/Garlic.cpp index d829fd71..b5586590 100644 --- a/Garlic.cpp +++ b/Garlic.cpp @@ -1,35 +1,77 @@ #include #include #include -#include -#include +#include "ElGamal.h" #include "RouterContext.h" #include "Timestamp.h" -#include "ElGamal.h" #include "Garlic.h" namespace i2p { - I2NPMessage * WrapI2NPMessage (const uint8_t * encryptionKey, I2NPMessage * msg) +namespace garlic +{ + GarlicRoutingSession::GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags): + m_Destination (destination), m_NumTags (numTags), m_NextTag (-1), m_SessionTags (0) { - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - // create ElGamal block - ElGamalBlock elGamal; - rnd.GenerateBlock (elGamal.sessionKey, 32); // session key - rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV - uint8_t iv[32]; // IV is first 16 bytes - CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); - - CryptoPP::CBC_Mode::Encryption encryption; - encryption.SetKeyWithIV (elGamal.sessionKey, 32, iv); + m_Rnd.GenerateBlock (m_SessionKey, 32); + if (m_NumTags > 0) + { + m_SessionTags = new uint8_t[m_NumTags*32]; + for (int i = 0; i < m_NumTags; i++) + m_Rnd.GenerateBlock (m_SessionTags + i*32, 32); + } + } + + GarlicRoutingSession::~GarlicRoutingSession () + { + delete[] m_SessionTags; + } + I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg) + { I2NPMessage * m = NewI2NPMessage (); + size_t len = 0; uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length - i2p::crypto::ElGamalEncrypt (encryptionKey, (uint8_t *)&elGamal, sizeof(elGamal), buf, true); - buf += 514; + if (m_NextTag < 0) // new session + { + // create ElGamal block + ElGamalBlock elGamal; + memcpy (elGamal.sessionKey, m_SessionKey, 32); + m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV + uint8_t iv[32]; // IV is first 16 bytes + CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); + i2p::crypto::ElGamalEncrypt (m_Destination->GetEncryptionPublicKey (), (uint8_t *)&elGamal, sizeof(elGamal), buf, true); + buf += 514; + // AES block + m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); + len += 514 + CreateAESBlock (buf, msg); + } + else // existing session + { + // session tag + memcpy (buf, m_SessionTags + m_NextTag*32, 32); + buf += 32; + uint8_t iv[32]; // IV is first 16 bytes + CryptoPP::SHA256().CalculateDigest(iv, m_SessionTags + m_NextTag*32, 32); + m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); + // AES block + len += 32 + CreateAESBlock (buf, msg); + } + m_NextTag++; + *(uint32_t *)(m->GetPayload ()) = htobe32 (len); + m->len += len + 4; + FillI2NPMessageHeader (m, eI2NPGarlic); + DeleteI2NPMessage (msg); + return m; + } + + size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, I2NPMessage * msg) + { size_t blockSize = 0; - *(uint16_t *)buf = 0; // tag count + *(uint16_t *)buf = htobe16 (m_NumTags); // tag count blockSize += 2; + memcpy (buf + blockSize, m_SessionTags, m_NumTags*32); // tags + blockSize += m_NumTags*32; uint32_t * payloadSize = (uint32_t *)(buf + blockSize); blockSize += 4; uint8_t * payloadHash = buf + blockSize; @@ -43,18 +85,13 @@ namespace i2p size_t rem = blockSize % 16; if (rem) blockSize += (16-rem); //padding - encryption.ProcessData(buf, buf, blockSize); - - *(uint32_t *)(m->GetPayload ()) = htobe32 (blockSize + 514); - m->len += blockSize + 514 + 4; - FillI2NPMessageHeader (m, eI2NPGarlic); - DeleteI2NPMessage (msg); - return m; + m_Encryption.ProcessData(buf, buf, blockSize); + return blockSize; } - size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg) + size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg) { - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec size_t size = 0; payload[size] = 1; // 1 clove size++; @@ -62,7 +99,7 @@ namespace i2p size++; memcpy (payload + size, msg->GetBuffer (), msg->GetLength ()); size += msg->GetLength (); - *(uint32_t *)(payload + size) = htobe32 (1011); // CloveID + *(uint32_t *)(payload + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID size += 4; *(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of clove size += 8; @@ -70,10 +107,46 @@ namespace i2p size += 3; memset (payload + size, 0, 3); // certificate of message size += 3; - *(uint32_t *)(payload + size) = htobe32 (2022); // MessageID + *(uint32_t *)(payload + size) = htobe32 (m_Rnd.GenerateWord32 ()); // MessageID size += 4; *(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of message size += 8; return size; } -} \ No newline at end of file + + GarlicRouting routing; + GarlicRouting::GarlicRouting () + { + } + + GarlicRouting::~GarlicRouting () + { + for (auto it: m_Sessions) + delete it.second; + m_Sessions.clear (); + } + + I2NPMessage * GarlicRouting::WrapSingleMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * msg) + { + if (!destination) return nullptr; + std::string dest ((const char *)destination->GetIdentHash (), 32); + auto it = m_Sessions.find (dest); + GarlicRoutingSession * session = nullptr; + if (it != m_Sessions.end ()) + session = it->second; + if (!session) + { + session = new GarlicRoutingSession (destination, 4); // TODO: change it later + m_Sessions[dest] = session; + } + + I2NPMessage * ret = session->WrapSingleMessage (msg); + if (session->GetNumRemainingSessionTags () <= 0) + { + m_Sessions.erase (dest); + delete session; + } + return ret; + } +} +} diff --git a/Garlic.h b/Garlic.h index 0518f36c..cda55a04 100644 --- a/Garlic.h +++ b/Garlic.h @@ -2,10 +2,19 @@ #define GARLIC_H__ #include +#include +#include +#include +#include +#include #include "I2NPProtocol.h" +#include "LeaseSet.h" namespace i2p +{ +namespace garlic { + enum GarlicDeliveryType { eGarlicDeliveryTypeLocal = 0, @@ -22,9 +31,49 @@ namespace i2p uint8_t padding[158]; }; #pragma pack() + - I2NPMessage * WrapI2NPMessage (const uint8_t * encryptionKey, I2NPMessage * msg); - size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg); + class GarlicRoutingSession + { + public: + + GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags); + ~GarlicRoutingSession (); + I2NPMessage * WrapSingleMessage (I2NPMessage * msg); + int GetNumRemainingSessionTags () const { return m_NumTags - m_NextTag; }; + + private: + + size_t CreateAESBlock (uint8_t * buf, I2NPMessage * msg); + size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg); + + private: + + const i2p::data::RoutingDestination * m_Destination; + uint8_t m_SessionKey[32]; + int m_NumTags, m_NextTag; + uint8_t * m_SessionTags; // m_NumTags*32 bytes + + CryptoPP::CBC_Mode::Encryption m_Encryption; + CryptoPP::AutoSeededRandomPool m_Rnd; + }; + + class GarlicRouting + { + public: + + GarlicRouting (); + ~GarlicRouting (); + + I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * msg); + + private: + + std::map m_Sessions; + }; + + extern GarlicRouting routing; } +} #endif diff --git a/LeaseSet.cpp b/LeaseSet.cpp new file mode 100644 index 00000000..7d429057 --- /dev/null +++ b/LeaseSet.cpp @@ -0,0 +1,33 @@ +#include +#include "Log.h" +#include "RouterInfo.h" +#include "LeaseSet.h" + +namespace i2p +{ +namespace data +{ + LeaseSet::LeaseSet (const uint8_t * buf, int len) + { +#pragma pack(1) + struct H + { + RouterIdentity destination; + uint8_t encryptionKey[256]; + uint8_t signingKey[128]; + uint8_t num; + }; +#pragma pack () + + const H * header = (const H *)buf; + CryptoPP::SHA256().CalculateDigest(m_IdentHash, (uint8_t *)&header->destination, sizeof (RouterIdentity)); + memcpy (m_EncryptionKey, header->encryptionKey, 256); + LogPrint ("LeaseSet num=", (int)header->num); + + for (int i = 0; i < header->num; i++) + { + m_Leases.push_back (*(Lease *)(buf + sizeof (H))); + } + } +} +} diff --git a/LeaseSet.h b/LeaseSet.h new file mode 100644 index 00000000..1b9f445d --- /dev/null +++ b/LeaseSet.h @@ -0,0 +1,46 @@ +#ifndef LEASE_SET_H__ +#define LEASE_SET_H__ + +#include +#include + +namespace i2p +{ +namespace data +{ +#pragma pack(1) + struct Lease + { + uint8_t tunnelGateway[32]; + uint32_t tunnelID; + uint64_t endDate; + }; +#pragma pack() + + class RoutingDestination // TODO: move to separate file later + { + public: + virtual const uint8_t * GetIdentHash () const = 0; + virtual const uint8_t * GetEncryptionPublicKey () const = 0; + }; + + class LeaseSet: public RoutingDestination + { + public: + + LeaseSet (const uint8_t * buf, int len); + + // implements RoutingDestination + const uint8_t * GetIdentHash () const { return m_IdentHash; }; + const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; }; + + private: + + std::list m_Leases; + uint8_t m_IdentHash[32]; + uint8_t m_EncryptionKey[256]; + }; +} +} + +#endif diff --git a/RouterInfo.h b/RouterInfo.h index 31fc0713..774a25bb 100644 --- a/RouterInfo.h +++ b/RouterInfo.h @@ -7,6 +7,7 @@ #include #include #include +#include "LeaseSet.h" namespace i2p { @@ -23,7 +24,7 @@ namespace data #pragma pack () - class RouterInfo + class RouterInfo: public RoutingDestination { public: @@ -50,7 +51,6 @@ namespace data const RouterIdentity& GetRouterIdentity () const { return m_RouterIdentity; }; void SetRouterIdentity (const RouterIdentity& identity); - const uint8_t * GetIdentHash () const { return m_IdentHash; }; const char * GetIdentHashBase64 () const { return m_IdentHashBase64; }; const std::vector
& GetAddresses () const { return m_Addresses; }; Address * GetNTCPAddress (); @@ -67,6 +67,10 @@ namespace data bool IsUpdated () const { return m_IsUpdated; }; void SetUpdated (bool updated) { m_IsUpdated = updated; }; + + // implements RoutingDestination + const uint8_t * GetIdentHash () const { return m_IdentHash; }; + const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity.publicKey; }; private: