diff --git a/CryptoConst.cpp b/CryptoConst.cpp new file mode 100644 index 00000000..045c8989 --- /dev/null +++ b/CryptoConst.cpp @@ -0,0 +1,66 @@ +#include +#include "CryptoConst.h" + +namespace i2p +{ +namespace crypto +{ + const uint8_t elgp_[256]= + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + const CryptoPP::Integer elgp (elgp_, 256); + const CryptoPP::Integer elgg (2); + + const uint8_t dsap_[128]= + { + 0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c, + 0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15, + 0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23, + 0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c, + 0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8, + 0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c, + 0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11, + 0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93 + }; + + const uint8_t dsaq_[20]= + { + 0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d, + 0x68, 0x40, 0x46, 0xb7 + }; + + const uint8_t dsag_[128]= + { + 0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0, + 0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81, + 0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2, + 0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52, + 0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc, + 0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a, + 0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c, + 0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82 + }; + + const CryptoPP::Integer dsap (dsap_, 128); + const CryptoPP::Integer dsaq (dsaq_, 20); + const CryptoPP::Integer dsag (dsag_, 128); +} +} + diff --git a/CryptoConst.h b/CryptoConst.h index df31b494..f96467f5 100644 --- a/CryptoConst.h +++ b/CryptoConst.h @@ -7,67 +7,15 @@ namespace i2p { namespace crypto { - // DH - - inline const CryptoPP::Integer& get_elgp () - { - static const CryptoPP::Integer elgp_ ( - "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" - "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" - "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" - "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" - "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" - "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" - "83655D23DCA3AD961C62F356208552BB9ED529077096966D" - "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" - "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" - "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" - "15728E5A8AACAA68FFFFFFFFFFFFFFFF" - "h"); - return elgp_; - } - #define elgp get_elgp() + // DH + extern const CryptoPP::Integer elgp; + extern const CryptoPP::Integer elgg; - inline const CryptoPP::Integer& get_elgg () - { - static const CryptoPP::Integer elgg_ (2); - return elgg_; - } - #define elgg get_elgg() // DSA - inline const CryptoPP::Integer& get_dsap () - { - static const CryptoPP::Integer dsap_ ( - "9c05b2aa960d9b97b8931963c9cc9e8c3026e9b8ed92fad0a69cc886d5bf8015fcadae31" - "a0ad18fab3f01b00a358de237655c4964afaa2b337e96ad316b9fb1cc564b5aec5b69a9f" - "f6c3e4548707fef8503d91dd8602e867e6d35d2235c1869ce2479c3b9d5401de04e0727f" - "b33d6511285d4cf29538d9e3b6051f5b22cc1c93" - "h"); - return dsap_; - } - #define dsap get_dsap() - - inline const CryptoPP::Integer& get_dsaq () - { - static const CryptoPP::Integer dsaq_ ( - "a5dfc28fef4ca1e286744cd8eed9d29d684046b7" - "h"); - return dsaq_; - } - #define dsaq get_dsaq() - - inline const CryptoPP::Integer& get_dsag () - { - static const CryptoPP::Integer dsag_ ( - "c1f4d27d40093b429e962d7223824e0bbc47e7c832a39236fc683af84889581075ff9082" - "ed32353d4374d7301cda1d23c431f4698599dda02451824ff369752593647cc3ddc197de" - "985e43d136cdcfc6bd5409cd2f450821142a5e6f8eb1c3ab5d0484b8129fcf17bce4f7f3" - "3321c3cb3dbb14a905e7b2b3e93be4708cbcc82" - "h"); - return dsag_; - } - #define dsag get_dsag() + extern const CryptoPP::Integer dsap; + extern const CryptoPP::Integer dsaq; + extern const CryptoPP::Integer dsag; } } diff --git a/Daemon.cpp b/Daemon.cpp index e0c93cb3..f5ee6549 100644 --- a/Daemon.cpp +++ b/Daemon.cpp @@ -53,7 +53,7 @@ namespace i2p //TODO: This is an ugly workaround. fix it. //TODO: Autodetect public IP. i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"), - i2p::util::config::GetArg("-port", 17070)); + i2p::util::config::GetArg("-port", 17007)); if (isLogging == 1) { @@ -63,12 +63,7 @@ namespace i2p #else logfile_path.append("\\debug.log"); #endif - logfile.open(logfile_path, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); - - if (!logfile.is_open()) - exit(-17); - - LogPrint("Logging to file enabled."); + g_Log.SetLogFile (logfile_path); LogPrint("CMD parameters:"); for (int i = 0; i < argc; ++i) diff --git a/Daemon.h b/Daemon.h index 4e27d108..f8f98467 100644 --- a/Daemon.h +++ b/Daemon.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #ifdef _WIN32 #define Daemon i2p::util::DaemonWin32::Instance() @@ -24,8 +24,6 @@ namespace i2p int running; - std::ofstream logfile; - protected: Daemon_Singleton(); virtual ~Daemon_Singleton(); diff --git a/Garlic.cpp b/Garlic.cpp index b4b4623a..85294271 100644 --- a/Garlic.cpp +++ b/Garlic.cpp @@ -20,6 +20,7 @@ namespace garlic { // create new session tags and session key m_Rnd.GenerateBlock (m_SessionKey, 32); + m_Encryption.SetKey (m_SessionKey); if (m_NumTags > 0) { m_SessionTags = new uint8_t[m_NumTags*32]; @@ -77,7 +78,7 @@ namespace garlic uint8_t iv[32]; // IV is first 16 bytes CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); m_Destination.GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true); - m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); + m_Encryption.SetIV (iv); buf += 514; len += 514; } @@ -87,7 +88,7 @@ namespace garlic memcpy (buf, m_SessionTags + m_NextTag*32, 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); + m_Encryption.SetIV (iv); buf += 32; len += 32; @@ -132,7 +133,7 @@ namespace garlic size_t rem = blockSize % 16; if (rem) blockSize += (16-rem); //padding - m_Encryption.ProcessData(buf, buf, blockSize); + m_Encryption.Encrypt(buf, blockSize, buf); return blockSize; } @@ -248,6 +249,9 @@ namespace garlic for (auto it: m_Sessions) delete it.second; m_Sessions.clear (); + for (auto it: m_SessionDecryptions) + delete it; + m_SessionDecryptions.clear (); } I2NPMessage * GarlicRouting::WrapSingleMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg) @@ -298,13 +302,12 @@ namespace garlic if (it != m_SessionTags.end ()) { // existing session - std::string sessionKey (it->second); - m_SessionTags.erase (it); // tag might be used only once uint8_t iv[32]; // IV is first 16 bytes CryptoPP::SHA256().CalculateDigest(iv, buf, 32); - m_Decryption.SetKeyWithIV ((uint8_t *)sessionKey.c_str (), 32, iv); // tag is mapped to 32 bytes key - m_Decryption.ProcessData(buf + 32, buf + 32, length - 32); - HandleAESBlock (buf + 32, length - 32, (uint8_t *)sessionKey.c_str ()); + it->second->SetIV (iv); + it->second->Decrypt (buf + 32, length - 32, buf + 32); + HandleAESBlock (buf + 32, length - 32, it->second); + m_SessionTags.erase (it); // tag might be used only once } else { @@ -317,11 +320,14 @@ namespace garlic pool ? pool->GetEncryptionPrivateKey () : i2p::context.GetPrivateKey (), buf, (uint8_t *)&elGamal, true)) { + i2p::crypto::CBCDecryption * decryption = new i2p::crypto::CBCDecryption; + m_SessionDecryptions.push_back (decryption); + decryption->SetKey (elGamal.sessionKey); uint8_t iv[32]; // IV is first 16 bytes CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); - m_Decryption.SetKeyWithIV (elGamal.sessionKey, 32, iv); - m_Decryption.ProcessData(buf + 514, buf + 514, length - 514); - HandleAESBlock (buf + 514, length - 514, elGamal.sessionKey); + decryption->SetIV (iv); + decryption->Decrypt(buf + 514, length - 514, buf + 514); + HandleAESBlock (buf + 514, length - 514, decryption); } else LogPrint ("Failed to decrypt garlic"); @@ -329,12 +335,12 @@ namespace garlic DeleteI2NPMessage (msg); } - void GarlicRouting::HandleAESBlock (uint8_t * buf, size_t len, uint8_t * sessionKey) + void GarlicRouting::HandleAESBlock (uint8_t * buf, size_t len, i2p::crypto::CBCDecryption * decryption) { uint16_t tagCount = be16toh (*(uint16_t *)buf); buf += 2; for (int i = 0; i < tagCount; i++) - m_SessionTags[std::string ((const char *)(buf + i*32), 32)] = std::string ((const char *)sessionKey, 32); + m_SessionTags[std::string ((const char *)(buf + i*32), 32)] = decryption; buf += tagCount*32; uint32_t payloadSize = be32toh (*(uint32_t *)buf); if (payloadSize > len) diff --git a/Garlic.h b/Garlic.h index 03fd6fe6..7ffb4ce6 100644 --- a/Garlic.h +++ b/Garlic.h @@ -3,11 +3,11 @@ #include #include +#include #include #include -#include -#include #include +#include "aes.h" #include "I2NPProtocol.h" #include "LeaseSet.h" #include "Tunnel.h" @@ -68,7 +68,7 @@ namespace garlic uint8_t * m_SessionTags; // m_NumTags*32 bytes uint32_t m_TagsCreationTime; // seconds since epoch - CryptoPP::CBC_Mode::Encryption m_Encryption; + i2p::crypto::CBCEncryption m_Encryption; CryptoPP::AutoSeededRandomPool m_Rnd; }; @@ -93,7 +93,7 @@ namespace garlic void Run (); void ProcessGarlicMessage (I2NPMessage * msg); - void HandleAESBlock (uint8_t * buf, size_t len, uint8_t * sessionKey); + void HandleAESBlock (uint8_t * buf, size_t len, i2p::crypto::CBCDecryption * decryption); void HandleGarlicPayload (uint8_t * buf, size_t len); private: @@ -105,8 +105,8 @@ namespace garlic std::map m_Sessions; std::map m_CreatedSessions; // msgID -> session // incoming session - std::map m_SessionTags; // tag -> key - CryptoPP::CBC_Mode::Decryption m_Decryption; + std::list m_SessionDecryptions; // multiple tags refer to one decyption + std::map m_SessionTags; // tag -> decryption }; extern GarlicRouting routing; diff --git a/HTTPServer.cpp b/HTTPServer.cpp index c2a235fa..038f6321 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -238,7 +238,7 @@ namespace util void HTTPConnection::FillContent (std::stringstream& s) { s << "Data path: " << i2p::util::filesystem::GetDataDir().string() << "
" << "
"; - s << "Our external address:" << "
" << "
"; + s << "Our external address:" << "
"; for (auto& address : i2p::context.GetRouterInfo().GetAddresses()) { switch (address.transportStyle) @@ -254,7 +254,10 @@ namespace util } s << address.host.to_string() << ":" << address.port << "
"; } - + s << "
Routers: " << i2p::data::netdb.GetNumRouters () << " "; + s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << " "; + s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "
"; + s << "

Tunnels

"; for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ()) { diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp index 0b34f9db..778d9197 100644 --- a/I2NPProtocol.cpp +++ b/I2NPProtocol.cpp @@ -1,8 +1,6 @@ #include #include "I2PEndian.h" #include -#include -#include #include #include "ElGamal.h" #include "Timestamp.h" @@ -236,6 +234,42 @@ namespace i2p memcpy (record.toPeer, (const uint8_t *)router.GetIdentHash (), 16); } + bool HandleBuildRequestRecords (int num, I2NPBuildRequestRecordElGamalEncrypted * records, I2NPBuildRequestRecordClearText& clearText) + { + for (int i = 0; i < num; i++) + { + if (!memcmp (records[i].toPeer, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) + { + LogPrint ("Record ",i," is ours"); + + i2p::crypto::ElGamalDecrypt (i2p::context.GetPrivateKey (), records[i].encrypted, (uint8_t *)&clearText); + + i2p::tunnel::TransitTunnel * transitTunnel = + i2p::tunnel::CreateTransitTunnel ( + be32toh (clearText.receiveTunnel), + clearText.nextIdent, be32toh (clearText.nextTunnel), + clearText.layerKey, clearText.ivKey, + clearText.flag & 0x80, clearText.flag & 0x40); + i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); + // replace record to reply + I2NPBuildResponseRecord * reply = (I2NPBuildResponseRecord *)(records + i); + reply->ret = 0; + //TODO: fill filler + CryptoPP::SHA256().CalculateDigest(reply->hash, reply->padding, sizeof (reply->padding) + 1); // + 1 byte of ret + // encrypt reply + i2p::crypto::CBCEncryption encryption; + for (int j = 0; j < num; j++) + { + encryption.SetKey (clearText.replyKey); + encryption.SetIV (clearText.replyIV); + encryption.Encrypt((uint8_t *)(records + j), sizeof (records[j]), (uint8_t *)(records + j)); + } + return true; + } + } + return false; + } + void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) { int num = buf[0]; @@ -260,52 +294,43 @@ namespace i2p else { I2NPBuildRequestRecordElGamalEncrypted * records = (I2NPBuildRequestRecordElGamalEncrypted *)(buf+1); - for (int i = 0; i < num; i++) - { - if (!memcmp (records[i].toPeer, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) - { - LogPrint ("Record ",i," is ours"); - - I2NPBuildRequestRecordClearText clearText; - i2p::crypto::ElGamalDecrypt (i2p::context.GetPrivateKey (), records[i].encrypted, (uint8_t *)&clearText); - - i2p::tunnel::TransitTunnel * transitTunnel = - i2p::tunnel::CreateTransitTunnel ( - be32toh (clearText.receiveTunnel), - clearText.nextIdent, be32toh (clearText.nextTunnel), - clearText.layerKey, clearText.ivKey, - clearText.flag & 0x80, clearText.flag & 0x40); - i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); - // replace record to reply - I2NPBuildResponseRecord * reply = (I2NPBuildResponseRecord *)(records + i); - reply->ret = 0; - //TODO: fill filler - CryptoPP::SHA256().CalculateDigest(reply->hash, reply->padding, sizeof (reply->padding) + 1); // + 1 byte of ret - // encrypt reply - CryptoPP::CBC_Mode::Encryption encryption; - for (int j = 0; j < num; j++) - { - encryption.SetKeyWithIV (clearText.replyKey, 32, clearText.replyIV); - encryption.ProcessData((uint8_t *)(records + j), (uint8_t *)(records + j), sizeof (records[j])); - } - - if (clearText.flag & 0x40) // we are endpoint of outboud tunnel - { - // so we send it to reply tunnel - i2p::transports.SendMessage (clearText.nextIdent, - CreateTunnelGatewayMsg (be32toh (clearText.nextTunnel), - eI2NPVariableTunnelBuildReply, buf, len, - be32toh (clearText.nextMessageID))); - } - else - i2p::transports.SendMessage (clearText.nextIdent, - CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, be32toh (clearText.nextMessageID))); - return; + I2NPBuildRequestRecordClearText clearText; + if (HandleBuildRequestRecords (num, records, clearText)) + { + if (clearText.flag & 0x40) // we are endpoint of outboud tunnel + { + // so we send it to reply tunnel + i2p::transports.SendMessage (clearText.nextIdent, + CreateTunnelGatewayMsg (be32toh (clearText.nextTunnel), + eI2NPVariableTunnelBuildReply, buf, len, + be32toh (clearText.nextMessageID))); } + else + i2p::transports.SendMessage (clearText.nextIdent, + CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, be32toh (clearText.nextMessageID))); } } } + void HandleTunnelBuildMsg (uint8_t * buf, size_t len) + { + I2NPBuildRequestRecordClearText clearText; + if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, (I2NPBuildRequestRecordElGamalEncrypted *)buf, clearText)) + { + if (clearText.flag & 0x40) // we are endpoint of outbound tunnel + { + // so we send it to reply tunnel + i2p::transports.SendMessage (clearText.nextIdent, + CreateTunnelGatewayMsg (be32toh (clearText.nextTunnel), + eI2NPTunnelBuildReply, buf, len, + be32toh (clearText.nextMessageID))); + } + else + i2p::transports.SendMessage (clearText.nextIdent, + CreateI2NPMessage (eI2NPTunnelBuild, buf, len, be32toh (clearText.nextMessageID))); + } + } + void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) { LogPrint ("VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID); @@ -444,6 +469,14 @@ namespace i2p LogPrint ("VariableTunnelBuildReply"); HandleVariableTunnelBuildReplyMsg (msgID, buf, size); break; + case eI2NPTunnelBuild: + LogPrint ("TunnelBuild"); + HandleTunnelBuildMsg (buf, size); + break; + case eI2NPTunnelBuildReply: + LogPrint ("TunnelBuildReply"); + // TODO: + break; case eI2NPDatabaseLookup: LogPrint ("DatabaseLookup"); HandleDatabaseLookupMsg (buf, size); diff --git a/I2NPProtocol.h b/I2NPProtocol.h index 5e7900aa..0b6e45d4 100644 --- a/I2NPProtocol.h +++ b/I2NPProtocol.h @@ -87,10 +87,14 @@ namespace i2p eI2NPTunnelData = 18, eI2NPTunnelGateway = 19, eI2NPData = 20, + eI2NPTunnelBuild = 21, + eI2NPTunnelBuildReply = 22, eI2NPVariableTunnelBuild = 23, eI2NPVariableTunnelBuildReply = 24 }; + const int NUM_TUNNEL_BUILD_RECORDS = 8; + namespace tunnel { class InboundTunnel; @@ -164,10 +168,12 @@ namespace tunnel void EncryptBuildRequestRecord (const i2p::data::RouterInfo& router, const I2NPBuildRequestRecordClearText& clearText, I2NPBuildRequestRecordElGamalEncrypted& record); - + + bool HandleBuildRequestRecords (int num, I2NPBuildRequestRecordElGamalEncrypted * records, I2NPBuildRequestRecordClearText& clearText); void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); - + void HandleTunnelBuildMsg (uint8_t * buf, size_t len); + I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf); I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload); diff --git a/I2PEndian.h b/I2PEndian.h index 661179cb..cc97debc 100644 --- a/I2PEndian.h +++ b/I2PEndian.h @@ -5,11 +5,27 @@ #include #elif __FreeBSD__ #include -#elif __MACH__ // Mac OS X -#include +#elif defined(__APPLE__) && defined(__MACH__) + +#include + +#define htobe16(x) OSSwapHostToBigInt16(x) +#define htole16(x) OSSwapHostToLittleInt16(x) +#define be16toh(x) OSSwapBigToHostInt16(x) +#define le16toh(x) OSSwapLittleToHostInt16(x) + +#define htobe32(x) OSSwapHostToBigInt32(x) +#define htole32(x) OSSwapHostToLittleInt32(x) +#define be32toh(x) OSSwapBigToHostInt32(x) +#define le32toh(x) OSSwapLittleToHostInt32(x) + +#define htobe64(x) OSSwapHostToBigInt64(x) +#define htole64(x) OSSwapHostToLittleInt64(x) +#define be64toh(x) OSSwapBigToHostInt64(x) +#define le64toh(x) OSSwapLittleToHostInt64(x) + #else #include - uint16_t htobe16(uint16_t int16); uint32_t htobe32(uint32_t int32); uint64_t htobe64(uint64_t int64); @@ -20,4 +36,5 @@ uint64_t be64toh(uint64_t big64); #endif -#endif // I2PENDIAN_H__ \ No newline at end of file +#endif // I2PENDIAN_H__ + diff --git a/Identity.cpp b/Identity.cpp index 5a59e26e..756eaec3 100644 --- a/Identity.cpp +++ b/Identity.cpp @@ -22,14 +22,14 @@ namespace data bool Identity::FromBase64 (const std::string& s) { - size_t count = Base64ToByteStream (s.c_str(), s.length(), reinterpret_cast (this), sizeof (Identity)); + size_t count = Base64ToByteStream (s.c_str(), s.length(), publicKey, sizeof (Identity)); return count == sizeof(Identity); } - IdentHash Identity::Hash() + IdentHash Identity::Hash() const { IdentHash hash; - CryptoPP::SHA256().CalculateDigest(reinterpret_cast(&hash), reinterpret_cast (this), sizeof (Identity)); + CryptoPP::SHA256().CalculateDigest(hash, publicKey, sizeof (Identity)); return hash; } @@ -40,11 +40,11 @@ namespace data return *this; } - bool IdentHash::FromBase32(const std::string& s) - { - size_t count = Base32ToByteStream(s.c_str(), s.length(), m_Hash, sizeof(m_Hash)); - return count == sizeof(m_Hash); - } + bool IdentHash::FromBase32(const std::string& s) + { + size_t count = Base32ToByteStream(s.c_str(), s.length(), m_Hash, sizeof(m_Hash)); + return count == sizeof(m_Hash); + } Keys CreateRandomKeys () { @@ -98,12 +98,11 @@ namespace data XORMetric operator^(const RoutingKey& key1, const RoutingKey& key2) { - // TODO: implementation depends on CPU XORMetric m; - ((uint64_t *)m.metric)[0] = ((uint64_t *)key1.hash)[0] ^ ((uint64_t *)key2.hash)[0]; - ((uint64_t *)m.metric)[1] = ((uint64_t *)key1.hash)[1] ^ ((uint64_t *)key2.hash)[1]; - ((uint64_t *)m.metric)[2] = ((uint64_t *)key1.hash)[2] ^ ((uint64_t *)key2.hash)[2]; - ((uint64_t *)m.metric)[3] = ((uint64_t *)key1.hash)[3] ^ ((uint64_t *)key2.hash)[3]; + m.metric_ll[0] = key1.hash_ll[0] ^ key2.hash_ll[0]; + m.metric_ll[1] = key1.hash_ll[1] ^ key2.hash_ll[1]; + m.metric_ll[2] = key1.hash_ll[2] ^ key2.hash_ll[2]; + m.metric_ll[3] = key1.hash_ll[3] ^ key2.hash_ll[3]; return m; } } diff --git a/Identity.h b/Identity.h index be9ae901..9c91e9e6 100644 --- a/Identity.h +++ b/Identity.h @@ -34,8 +34,8 @@ namespace data uint8_t certificate[3]; Identity& operator=(const Keys& keys); - bool FromBase64(const std::string&); - IdentHash Hash(); + bool FromBase64(const std::string& ); + IdentHash Hash() const; }; struct PrivateKeys // for eepsites @@ -44,6 +44,10 @@ namespace data uint8_t privateKey[256]; uint8_t signingPrivateKey[20]; + PrivateKeys () = default; + PrivateKeys (const PrivateKeys& ) = default; + PrivateKeys (const Keys& keys) { *this = keys; }; + PrivateKeys& operator=(const Keys& keys); }; @@ -74,7 +78,7 @@ namespace data bool operator== (const IdentHash& other) const { return !memcmp (m_Hash, other.m_Hash, 32); }; bool operator< (const IdentHash& other) const { return memcmp (m_Hash, other.m_Hash, 32) < 0; }; - bool FromBase32(const std::string&); + bool FromBase32(const std::string&); private: @@ -85,14 +89,19 @@ namespace data void CreateRandomDHKeysPair (DHKeysPair * keys); // for transport sessions // kademlia - struct RoutingKey + union RoutingKey { uint8_t hash[32]; + uint64_t hash_ll[4]; }; struct XORMetric { - uint8_t metric[32]; + union + { + uint8_t metric[32]; + uint64_t metric_ll[4]; + }; void SetMin () { memset (metric, 0, 32); }; void SetMax () { memset (metric, 0xFF, 32); }; diff --git a/Log.cpp b/Log.cpp index 92fd8a25..4a88f2ba 100644 --- a/Log.cpp +++ b/Log.cpp @@ -1,20 +1,29 @@ #include "Log.h" -#include "Daemon.h" - Log g_Log; void LogMsg::Process() { - if (Daemon.isLogging == 1 && Daemon.logfile.is_open()) - Daemon.logfile << s.str(); - output << s.str(); + + std::cout << s.str (); // TODO: delete later } void Log::Flush () { - if (Daemon.isLogging == 1 && Daemon.logfile.is_open()) - Daemon.logfile.flush(); + if (m_LogFile) + m_LogFile->flush(); } +void Log::SetLogFile (const std::string& fullFilePath) +{ + if (m_LogFile) delete m_LogFile; + m_LogFile = new std::ofstream (fullFilePath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); + if (m_LogFile->is_open ()) + LogPrint("Logging to file ", fullFilePath, " enabled."); + else + { + delete m_LogFile; + m_LogFile = nullptr; + } +} diff --git a/Log.h b/Log.h index a5856ba7..f3e7ae53 100644 --- a/Log.h +++ b/Log.h @@ -1,8 +1,10 @@ #ifndef LOG_H__ #define LOG_H__ +#include #include #include +#include #include #include "Queue.h" @@ -20,11 +22,19 @@ class Log: public i2p::util::MsgQueue { public: - Log () { SetOnEmpty (std::bind (&Log::Flush, this)); }; + Log (): m_LogFile (nullptr) { SetOnEmpty (std::bind (&Log::Flush, this)); }; + ~Log () { delete m_LogFile; }; + + void SetLogFile (const std::string& fullFilePath); + std::ofstream * GetLogFile () const { return m_LogFile; }; private: void Flush (); + + private: + + std::ofstream * m_LogFile; }; extern Log g_Log; @@ -45,7 +55,7 @@ void LogPrint (std::stringstream& s, TValue arg, TArgs... args) template void LogPrint (TArgs... args) { - LogMsg * msg = new LogMsg (); + LogMsg * msg = g_Log.GetLogFile () ? new LogMsg (*g_Log.GetLogFile ()) : new LogMsg (); LogPrint (msg->s, args...); msg->s << std::endl; g_Log.Put (msg); diff --git a/Makefile b/Makefile index 33f513bf..149ade28 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,21 @@ CC = g++ CFLAGS = -g -Wall -std=c++0x -OBJECTS = obj/i2p.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transports.o \ +OBJECTS = obj/CryptoConst.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transports.o \ obj/RouterContext.o obj/NetDb.o obj/LeaseSet.o obj/Tunnel.o obj/TunnelEndpoint.o \ obj/TunnelGateway.o obj/TransitTunnel.o obj/I2NPProtocol.o obj/Log.o obj/Garlic.o \ obj/HTTPServer.o obj/Streaming.o obj/Identity.o obj/SSU.o obj/util.o obj/Reseed.o \ - obj/UPnP.o obj/TunnelPool.o obj/HTTPProxy.o obj/AddressBook.o \ - obj/Daemon.o obj/DaemonLinux.o obj/SSUData.o + obj/UPnP.o obj/TunnelPool.o obj/HTTPProxy.o obj/AddressBook.o obj/Daemon.o \ + obj/DaemonLinux.o obj/SSUData.o obj/i2p.o obj/aes.o INCFLAGS = LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread LIBS = +#check if AES-NI is supported by CPU +ifneq ($(shell grep -c aes /proc/cpuinfo),0) + CPU_FLAGS = -DAESNI +endif + all: obj i2p i2p: $(OBJECTS:obj/%=obj/%) @@ -20,7 +25,7 @@ i2p: $(OBJECTS:obj/%=obj/%) .SUFFIXES: .c .cc .C .cpp .o obj/%.o : %.cpp - $(CC) -o $@ $< -c $(CFLAGS) $(INCFLAGS) + $(CC) -o $@ $< -c $(CFLAGS) $(INCFLAGS) $(CPU_FLAGS) obj: mkdir -p obj diff --git a/Makefile.osx b/Makefile.osx new file mode 100644 index 00000000..e294971b --- /dev/null +++ b/Makefile.osx @@ -0,0 +1,43 @@ +#CC = clang++ +CC = g++ +CFLAGS = -g -Wall -std=c++11 -lstdc++ +OBJECTS = obj/CryptoConst.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transports.o \ + obj/RouterContext.o obj/NetDb.o obj/LeaseSet.o obj/Tunnel.o obj/TunnelEndpoint.o \ + obj/TunnelGateway.o obj/TransitTunnel.o obj/I2NPProtocol.o obj/Log.o obj/Garlic.o \ + obj/HTTPServer.o obj/Streaming.o obj/Identity.o obj/SSU.o obj/util.o obj/Reseed.o \ + obj/UPnP.o obj/TunnelPool.o obj/HTTPProxy.o obj/AddressBook.o obj/Daemon.o \ + obj/DaemonLinux.o obj/SSUData.o obj/i2p.o obj/aes.o +INCFLAGS = -DCRYPTOPP_DISABLE_ASM +LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread +LIBS = + +#check if AES-NI is supported by CPU +ifneq ($(shell grep -c aes /proc/cpuinfo),0) + CPU_FLAGS = -DAESNI +endif + +# Apple Mac OSX +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) +endif + +all: obj i2p + +i2p: $(OBJECTS:obj/%=obj/%) + $(CC) -o $@ $^ $(LDFLAGS) $(LIBS) + +.SUFFIXES: +.SUFFIXES: .c .cc .C .cpp .o + +obj/%.o : %.cpp + $(CC) -o $@ $< -c $(CFLAGS) $(INCFLAGS) $(CPU_FLAGS) + +obj: + mkdir -p obj + +clean: + rm -fr obj i2p + +.PHONY: all +.PHONY: clean + diff --git a/NTCPSession.cpp b/NTCPSession.cpp index 712e8232..9930dead 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -183,10 +183,12 @@ namespace ntcp uint8_t aesKey[32]; CreateAESKey (m_Phase1.pubKey, aesKey); - m_Encryption.SetKeyWithIV (aesKey, 32, y + 240); - m_Decryption.SetKeyWithIV (aesKey, 32, m_Phase1.HXxorHI + 16); + m_Encryption.SetKey (aesKey); + m_Encryption.SetIV (y + 240); + m_Decryption.SetKey (aesKey); + m_Decryption.SetIV (m_Phase1.HXxorHI + 16); - m_Encryption.ProcessData((uint8_t *)&m_Phase2.encrypted, (uint8_t *)&m_Phase2.encrypted, sizeof(m_Phase2.encrypted)); + m_Encryption.Encrypt ((uint8_t *)&m_Phase2.encrypted, sizeof(m_Phase2.encrypted), (uint8_t *)&m_Phase2.encrypted); boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Phase2, sizeof (m_Phase2)), boost::asio::transfer_all (), boost::bind(&NTCPSession::HandlePhase2Sent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, tsB)); @@ -222,10 +224,12 @@ namespace ntcp uint8_t aesKey[32]; CreateAESKey (m_Phase2.pubKey, aesKey); - m_Decryption.SetKeyWithIV (aesKey, 32, m_Phase2.pubKey + 240); - m_Encryption.SetKeyWithIV (aesKey, 32, m_Phase1.HXxorHI + 16); + m_Decryption.SetKey (aesKey); + m_Decryption.SetIV (m_Phase2.pubKey + 240); + m_Encryption.SetKey (aesKey); + m_Encryption.SetIV (m_Phase1.HXxorHI + 16); - m_Decryption.ProcessData((uint8_t *)&m_Phase2.encrypted, (uint8_t *)&m_Phase2.encrypted, sizeof(m_Phase2.encrypted)); + m_Decryption.Decrypt((uint8_t *)&m_Phase2.encrypted, sizeof(m_Phase2.encrypted), (uint8_t *)&m_Phase2.encrypted); // verify uint8_t xy[512], hxy[32]; memcpy (xy, m_DHKeysPair->publicKey, 256); @@ -256,7 +260,7 @@ namespace ntcp s.tsB = m_Phase2.encrypted.timestamp; i2p::context.Sign ((uint8_t *)&s, sizeof (s), m_Phase3.signature); - m_Encryption.ProcessData((uint8_t *)&m_Phase3, (uint8_t *)&m_Phase3, sizeof(m_Phase3)); + m_Encryption.Encrypt((uint8_t *)&m_Phase3, sizeof(m_Phase3), (uint8_t *)&m_Phase3); boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Phase3, sizeof (m_Phase3)), boost::asio::transfer_all (), boost::bind(&NTCPSession::HandlePhase3Sent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, tsA)); @@ -288,7 +292,7 @@ namespace ntcp else { LogPrint ("Phase 3 received: ", bytes_transferred); - m_Decryption.ProcessData((uint8_t *)&m_Phase3, (uint8_t *)&m_Phase3, sizeof(m_Phase3)); + m_Decryption.Decrypt ((uint8_t *)&m_Phase3, sizeof(m_Phase3), (uint8_t *)&m_Phase3); m_RemoteRouterInfo.SetRouterIdentity (m_Phase3.ident); SignedData s; @@ -321,7 +325,7 @@ namespace ntcp s.tsA = m_Phase3.timestamp; s.tsB = tsB; i2p::context.Sign ((uint8_t *)&s, sizeof (s), m_Phase4.signature); - m_Encryption.ProcessData((uint8_t *)&m_Phase4, (uint8_t *)&m_Phase4, sizeof(m_Phase4)); + m_Encryption.Encrypt ((uint8_t *)&m_Phase4, sizeof(m_Phase4), (uint8_t *)&m_Phase4); boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Phase4, sizeof (m_Phase4)), boost::asio::transfer_all (), boost::bind(&NTCPSession::HandlePhase4Sent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); @@ -355,7 +359,7 @@ namespace ntcp else { LogPrint ("Phase 4 received: ", bytes_transferred); - m_Decryption.ProcessData((uint8_t *)&m_Phase4, (uint8_t *)&m_Phase4, sizeof(m_Phase4)); + m_Decryption.Decrypt((uint8_t *)&m_Phase4, sizeof(m_Phase4), (uint8_t *)&m_Phase4); // verify signature SignedData s; @@ -426,7 +430,7 @@ namespace ntcp m_NextMessage = i2p::NewI2NPMessage (); m_NextMessageOffset = 0; - m_Decryption.ProcessData (m_NextMessage->buf, encrypted, 16); + m_Decryption.Decrypt (encrypted, m_NextMessage->buf); uint16_t dataSize = be16toh (*(uint16_t *)m_NextMessage->buf); if (dataSize) { @@ -446,7 +450,7 @@ namespace ntcp } else // message continues { - m_Decryption.ProcessData (m_NextMessage->buf + m_NextMessageOffset, encrypted, 16); + m_Decryption.Decrypt (encrypted, m_NextMessage->buf + m_NextMessageOffset); m_NextMessageOffset += 16; } @@ -490,7 +494,7 @@ namespace ntcp m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding); int l = len + padding + 6; - m_Encryption.ProcessData(sendBuffer, sendBuffer, l); + m_Encryption.Encrypt(sendBuffer, l, sendBuffer); boost::asio::async_write (m_Socket, boost::asio::buffer (sendBuffer, l), boost::asio::transfer_all (), boost::bind(&NTCPSession::HandleSent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, msg)); diff --git a/NTCPSession.h b/NTCPSession.h index 83396e11..f453c417 100644 --- a/NTCPSession.h +++ b/NTCPSession.h @@ -7,6 +7,7 @@ #include #include #include +#include "aes.h" #include "Identity.h" #include "RouterInfo.h" #include "I2NPProtocol.h" @@ -123,8 +124,8 @@ namespace ntcp bool m_IsEstablished; i2p::data::DHKeysPair * m_DHKeysPair; // X - for client and Y - for server - CryptoPP::CBC_Mode::Decryption m_Decryption; - CryptoPP::CBC_Mode::Encryption m_Encryption; + i2p::crypto::CBCDecryption m_Decryption; + i2p::crypto::CBCEncryption m_Encryption; CryptoPP::Adler32 m_Adler; i2p::data::RouterInfo& m_RemoteRouterInfo; diff --git a/NetDb.cpp b/NetDb.cpp index 553ee52b..efd12c7c 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -122,7 +122,10 @@ namespace data } } else // if no new DatabaseStore coming, explore it - Explore (); + { + auto numRouters = m_RouterInfos.size (); + Explore (numRouters < 1500 ? 5 : 1); + } uint64_t ts = i2p::util::GetSecondsSinceEpoch (); if (ts - lastSave >= 60) // save routers and validate subscriptions every minute @@ -532,40 +535,54 @@ namespace data i2p::DeleteI2NPMessage (msg); } - void NetDb::Explore () - { + void NetDb::Explore (int numDestinations) + { auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr; - if (outbound && inbound) - { - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - uint8_t randomHash[32]; + bool throughTunnels = outbound && inbound; + + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + uint8_t randomHash[32]; + std::vector msgs; + std::set floodfills; + LogPrint ("Exploring new ", numDestinations, " routers ..."); + for (int i = 0; i < numDestinations; i++) + { rnd.GenerateBlock (randomHash, 32); RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true); - dest->SetLastOutboundTunnel (outbound); auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ()); - if (floodfill) + if (floodfill && !floodfills.count (floodfill)) // request floodfill only once { - LogPrint ("Exploring new routers ..."); - std::vector msgs; - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeRouter, - floodfill->GetIdentHash (), 0, - CreateDatabaseStoreMsg () // tell floodfill about us - }); - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeRouter, - floodfill->GetIdentHash (), 0, - dest->CreateRequestMessage (floodfill, inbound) // explore - }); - outbound->SendTunnelDataMsg (msgs); + floodfills.insert (floodfill); + if (throughTunnels) + { + dest->SetLastOutboundTunnel (outbound); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + floodfill->GetIdentHash (), 0, + CreateDatabaseStoreMsg () // tell floodfill about us + }); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + floodfill->GetIdentHash (), 0, + dest->CreateRequestMessage (floodfill, inbound) // explore + }); + } + else + { + dest->SetLastOutboundTunnel (nullptr); + dest->SetLastReplyTunnel (nullptr); + i2p::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); + } } else DeleteRequestedDestination (dest); - } + } + if (throughTunnels && msgs.size () > 0) + outbound->SendTunnelDataMsg (msgs); } void NetDb::Publish () @@ -616,7 +633,7 @@ namespace data } } - const RouterInfo * NetDb::GetRandomRouter (const RouterInfo * compatibleWith, uint8_t caps) const + const RouterInfo * NetDb::GetRandomRouter (const RouterInfo * compatibleWith) const { CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1); @@ -627,9 +644,8 @@ namespace data { if (i >= ind) { - if (!it.second->IsUnreachable () && - (!compatibleWith || it.second->IsCompatible (*compatibleWith)) && - (!caps || (it.second->GetCaps () & caps) == caps)) + if (!it.second->IsUnreachable () && !it.second->IsHidden () && + (!compatibleWith || it.second->IsCompatible (*compatibleWith))) return it.second; } else diff --git a/NetDb.h b/NetDb.h index 35fbe844..d9451f7c 100644 --- a/NetDb.h +++ b/NetDb.h @@ -33,6 +33,7 @@ namespace data void ClearExcludedPeers (); const RouterInfo * GetLastRouter () const { return m_LastRouter; }; const i2p::tunnel::InboundTunnel * GetLastReplyTunnel () const { return m_LastReplyTunnel; }; + void SetLastReplyTunnel (i2p::tunnel::InboundTunnel * tunnel) { m_LastReplyTunnel = tunnel; }; bool IsExploratory () const { return m_IsExploratory; }; bool IsLeaseSet () const { return m_IsLeaseSet; }; bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); }; @@ -75,9 +76,14 @@ namespace data void HandleDatabaseStoreMsg (uint8_t * buf, size_t len); void HandleDatabaseSearchReplyMsg (I2NPMessage * msg); - const RouterInfo * GetRandomRouter (const RouterInfo * compatibleWith = nullptr, uint8_t caps = 0) const; + const RouterInfo * GetRandomRouter (const RouterInfo * compatibleWith = nullptr) const; void PostI2NPMsg (I2NPMessage * msg); + + // for web interface + int GetNumRouters () const { return m_RouterInfos.size (); }; + int GetNumFloodfills () const { return m_Floodfills.size (); }; + int GetNumLeaseSets () const { return m_LeaseSets.size (); }; private: @@ -85,7 +91,7 @@ namespace data void Load (const char * directory); void SaveUpdated (const char * directory); void Run (); // exploratory thread - void Explore (); + void Explore (int numDestinations); void Publish (); void ValidateSubscriptions (); const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set& excluded) const; diff --git a/RouterInfo.cpp b/RouterInfo.cpp index da829c47..9ffe7edf 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -214,7 +214,10 @@ namespace data break; case 'C': m_Caps |= Caps::eSSUIntroducer; - break; + break; + case 'H': + m_Caps |= Caps::eHidden; + break; default: ; } cap++; diff --git a/RouterInfo.h b/RouterInfo.h index d730c5f8..9ce9b006 100644 --- a/RouterInfo.h +++ b/RouterInfo.h @@ -31,7 +31,8 @@ namespace data eHighBandwidth = 0x02, eReachable = 0x04, eSSUTesting = 0x08, - eSSUIntroducer = 0x10 + eSSUIntroducer = 0x10, + eHidden = 0x20 }; enum TransportStyle @@ -88,6 +89,8 @@ namespace data bool UsesIntroducer () const; bool IsIntroducer () const { return m_Caps & eSSUIntroducer; }; bool IsPeerTesting () const { return m_Caps & eSSUTesting; }; + bool IsHidden () const { return m_Caps & eHidden; }; + uint8_t GetCaps () const { return m_Caps; }; void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; diff --git a/SSU.cpp b/SSU.cpp index 32dbf681..c7b180ef 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -29,7 +29,7 @@ namespace ssu delete m_DHKeysPair; } - void SSUSession::CreateAESandMacKey (const uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey) + void SSUSession::CreateAESandMacKey (const uint8_t * pubKey) { CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); uint8_t sharedKey[256]; @@ -41,14 +41,14 @@ namespace ssu if (sharedKey[0] & 0x80) { - aesKey[0] = 0; - memcpy (aesKey + 1, sharedKey, 31); - memcpy (macKey, sharedKey + 31, 32); + m_SessionKey[0] = 0; + memcpy (m_SessionKey + 1, sharedKey, 31); + memcpy (m_MacKey, sharedKey + 31, 32); } else if (sharedKey[0]) { - memcpy (aesKey, sharedKey, 32); - memcpy (macKey, sharedKey + 32, 32); + memcpy (m_SessionKey, sharedKey, 32); + memcpy (m_MacKey, sharedKey + 32, 32); } else { @@ -64,10 +64,12 @@ namespace ssu } } - memcpy (aesKey, nonZero, 32); - CryptoPP::SHA256().CalculateDigest(macKey, nonZero, 64 - (nonZero - sharedKey)); + memcpy (m_SessionKey, nonZero, 32); + CryptoPP::SHA256().CalculateDigest(m_MacKey, nonZero, 64 - (nonZero - sharedKey)); } m_IsSessionKey = true; + m_SessionKeyEncryption.SetKey (m_SessionKey); + m_SessionKeyDecryption.SetKey (m_SessionKey); } void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) @@ -82,8 +84,13 @@ namespace ssu else { ScheduleTermination (); + // check for duplicate + const uint8_t * iv = ((SSUHeader *)buf)->iv; + if (m_ReceivedIVs.count (iv)) return; // duplicate detected + m_ReceivedIVs.insert (iv); + if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first - Decrypt (buf, len, m_SessionKey); + DecryptSessionKey (buf, len); else { // try intro key depending on side @@ -161,10 +168,9 @@ namespace ssu void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) { - m_State = eSessionStateRequestReceived; LogPrint ("Session request received"); m_RemoteEndpoint = senderEndpoint; - CreateAESandMacKey (buf + sizeof (SSUHeader), m_SessionKey, m_MacKey); + CreateAESandMacKey (buf + sizeof (SSUHeader)); SendSessionCreated (buf + sizeof (SSUHeader)); } @@ -176,13 +182,12 @@ namespace ssu return; } - m_State = eSessionStateCreatedReceived; LogPrint ("Session created received"); m_Timer.cancel (); // connect timer uint8_t signedData[532]; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * y = payload; - CreateAESandMacKey (y, m_SessionKey, m_MacKey); + CreateAESandMacKey (y); memcpy (signedData, m_DHKeysPair->publicKey, 256); // x memcpy (signedData + 256, y, 256); // y payload += 256; @@ -202,8 +207,8 @@ namespace ssu payload += 4; // relayTag payload += 4; // signed on time // decrypt DSA signature - m_Decryption.SetKeyWithIV (m_SessionKey, 32, ((SSUHeader *)buf)->iv); - m_Decryption.ProcessData (payload, payload, 48); + m_SessionKeyDecryption.SetIV (((SSUHeader *)buf)->iv); + m_SessionKeyDecryption.Decrypt (payload, 48, payload); // verify CryptoPP::DSA::PublicKey pubKey; pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, CryptoPP::Integer (m_RemoteRouter->GetRouterIdentity ().signingKey, 128)); @@ -216,9 +221,7 @@ namespace ssu void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len) { - m_State = eSessionStateConfirmedReceived; LogPrint ("Session confirmed received"); - m_State = eSessionStateEstablished; SendI2NPMessage (CreateDeliveryStatusMsg (0)); Established (); } @@ -242,8 +245,6 @@ namespace ssu CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); rnd.GenerateBlock (iv, 16); // random iv FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, 304, introKey, iv, introKey); - - m_State = eSessionStateRequestSent; m_Server.Send (buf, 304, m_RemoteEndpoint); } @@ -276,10 +277,7 @@ namespace ssu if (m_State == eSessionStateEstablished) FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, m_SessionKey, iv, m_MacKey); else - { - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, iKey, iv, iKey); - m_State = eSessionStateRelayRequestSent; - } + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, iKey, iv, iKey); m_Server.Send (buf, 96, m_RemoteEndpoint); } @@ -327,13 +325,12 @@ namespace ssu uint8_t iv[16]; rnd.GenerateBlock (iv, 16); // random iv // encrypt signature and 8 bytes padding with newly created session key - m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); - m_Encryption.ProcessData (payload, payload, 48); + m_SessionKeyEncryption.SetIV (iv); + m_SessionKeyEncryption.Encrypt (payload, 48, payload); // encrypt message with intro key - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, introKey, iv, introKey); - m_State = eSessionStateCreatedSent; - m_Server.Send (buf, 368, m_RemoteEndpoint); + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, introKey, iv, introKey); + Send (buf, 368); } void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress) @@ -371,8 +368,7 @@ namespace ssu rnd.GenerateBlock (iv, 16); // random iv // encrypt message with session key FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CONFIRMED, buf, 480, m_SessionKey, iv, m_MacKey); - m_State = eSessionStateConfirmedSent; - m_Server.Send (buf, 480, m_RemoteEndpoint); + Send (buf, 480); } void SSUSession::ProcessRelayRequest (uint8_t * buf, size_t len) @@ -499,15 +495,37 @@ namespace ssu header->time = htobe32 (i2p::util::GetSecondsSinceEpoch ()); uint8_t * encrypted = &header->flag; uint16_t encryptedLen = len - (encrypted - buf); - m_Encryption.SetKeyWithIV (aesKey, 32, iv); - encryptedLen = (encryptedLen>>4)<<4; // make sure 16 bytes boundary - m_Encryption.ProcessData (encrypted, encrypted, encryptedLen); + i2p::crypto::CBCEncryption encryption; + encryption.SetKey (aesKey); + encryption.SetIV (iv); + encryption.Encrypt (encrypted, encryptedLen, encrypted); // assume actual buffer size is 18 (16 + 2) bytes more memcpy (buf + len, iv, 16); *(uint16_t *)(buf + len + 16) = htobe16 (encryptedLen); i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac); } + void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len) + { + if (len < sizeof (SSUHeader)) + { + LogPrint ("Unexpected SSU packet length ", len); + return; + } + SSUHeader * header = (SSUHeader *)buf; + i2p::context.GetRandomNumberGenerator ().GenerateBlock (header->iv, 16); // random iv + m_SessionKeyEncryption.SetIV (header->iv); + header->flag = payloadType << 4; // MSB is 0 + header->time = htobe32 (i2p::util::GetSecondsSinceEpoch ()); + uint8_t * encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted); + // assume actual buffer size is 18 (16 + 2) bytes more + memcpy (buf + len, header->iv, 16); + *(uint16_t *)(buf + len + 16) = htobe16 (encryptedLen); + i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac); + } + void SSUSession::Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey) { if (len < sizeof (SSUHeader)) @@ -518,11 +536,29 @@ namespace ssu SSUHeader * header = (SSUHeader *)buf; uint8_t * encrypted = &header->flag; uint16_t encryptedLen = len - (encrypted - buf); - m_Decryption.SetKeyWithIV (aesKey, 32, header->iv); - encryptedLen = (encryptedLen>>4)<<4; // make sure 16 bytes boundary - m_Decryption.ProcessData (encrypted, encrypted, encryptedLen); + i2p::crypto::CBCDecryption decryption; + decryption.SetKey (aesKey); + decryption.SetIV (header->iv); + decryption.Decrypt (encrypted, encryptedLen, encrypted); } + void SSUSession::DecryptSessionKey (uint8_t * buf, size_t len) + { + if (len < sizeof (SSUHeader)) + { + LogPrint ("Unexpected SSU packet length ", len); + return; + } + SSUHeader * header = (SSUHeader *)buf; + uint8_t * encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + if (encryptedLen > 0) + { + m_SessionKeyDecryption.SetIV (header->iv); + m_SessionKeyDecryption.Decrypt (encrypted, encryptedLen, encrypted); + } + } + bool SSUSession::Validate (uint8_t * buf, size_t len, const uint8_t * macKey) { if (len < sizeof (SSUHeader)) @@ -602,7 +638,7 @@ namespace ssu if (!m_DelayedMessages.empty ()) { for (auto it :m_DelayedMessages) - Send (it); + m_Data.Send (it); m_DelayedMessages.clear (); } if (m_PeerTest && (m_RemoteRouter && m_RemoteRouter->IsPeerTesting ())) @@ -658,7 +694,7 @@ namespace ssu if (msg) { if (m_State == eSessionStateEstablished) - Send (msg); + m_Data.Send (msg); else m_DelayedMessages.push_back (msg); } @@ -771,100 +807,26 @@ namespace ssu memset (payload, 0, 6); // address and port always zero for Alice payload += 6; // address and port memcpy (payload, introKey, 32); // intro key - uint8_t iv[16]; - rnd.GenerateBlock (iv, 16); // random iv // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, m_SessionKey, iv, m_MacKey); - m_Server.Send (buf, 80, m_RemoteEndpoint); + FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80); + Send (buf, 80); } - void SSUSession::SendMsgAck (uint32_t msgID) - { - uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16 - uint8_t iv[16]; - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag - payload++; - *payload = 1; // number of ACKs - payload++; - *(uint32_t *)(payload) = htobe32 (msgID); // msgID - payload += 4; - *payload = 0; // number of fragments - - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - rnd.GenerateBlock (iv, 16); // random iv - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48, m_SessionKey, iv, m_MacKey); - m_Server.Send (buf, 48, m_RemoteEndpoint); - } void SSUSession::SendSesionDestroyed () { if (m_IsSessionKey) { - uint8_t buf[48 + 18], iv[16]; - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - rnd.GenerateBlock (iv, 16); // random iv + uint8_t buf[48 + 18]; // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey); - m_Server.Send (buf, 48, m_RemoteEndpoint); + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48); + Send (buf, 48); } } - void SSUSession::Send (i2p::I2NPMessage * msg) - { - uint32_t msgID = htobe32 (msg->ToSSU ()); - size_t payloadSize = SSU_MTU - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) - size_t len = msg->GetLength (); - uint8_t * msgBuf = msg->GetSSUHeader (); - - uint32_t fragmentNum = 0; - while (len > 0) - { - uint8_t buf[SSU_MTU + 18], iv[16], * payload = buf + sizeof (SSUHeader); - *payload = DATA_FLAG_WANT_REPLY; // for compatibility - payload++; - *payload = 1; // always 1 message fragment per message - payload++; - *(uint32_t *)payload = msgID; - payload += 4; - bool isLast = (len <= payloadSize); - size_t size = isLast ? len : payloadSize; - uint32_t fragmentInfo = (fragmentNum << 17); - if (isLast) - fragmentInfo |= 0x010000; - - fragmentInfo |= size; - fragmentInfo = htobe32 (fragmentInfo); - memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3); - payload += 3; - memcpy (payload, msgBuf, size); - - size += payload - buf; - if (size & 0x0F) // make sure 16 bytes boundary - size = ((size >> 4) + 1) << 4; // (/16 + 1)*16 - - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - rnd.GenerateBlock (iv, 16); // random iv - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size, m_SessionKey, iv, m_MacKey); - m_Server.Send (buf, size, m_RemoteEndpoint); - - if (!isLast) - { - len -= payloadSize; - msgBuf += payloadSize; - } - else - len = 0; - fragmentNum++; - } - } - void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len) { uint8_t buf[SSU_MTU + 18]; - uint8_t iv[16]; size_t msgSize = len + sizeof (SSUHeader); if (msgSize > SSU_MTU) { @@ -872,13 +834,16 @@ namespace ssu return; } memcpy (buf + sizeof (SSUHeader), payload, len); - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - rnd.GenerateBlock (iv, 16); // random iv // encrypt message with session key - FillHeaderAndEncrypt (type, buf, msgSize, m_SessionKey, iv, m_MacKey); - m_Server.Send (buf, msgSize, m_RemoteEndpoint); + FillHeaderAndEncrypt (type, buf, msgSize); + Send (buf, msgSize); } + void SSUSession::Send (const uint8_t * buf, size_t size) + { + m_Server.Send (buf, size, m_RemoteEndpoint); + } + SSUServer::SSUServer (int port): m_Thread (nullptr), m_Work (m_Service), m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (m_Service, m_Endpoint) { @@ -941,7 +906,7 @@ namespace ssu return nullptr; } - void SSUServer::Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to) + void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to) { m_Socket.send_to (boost::asio::buffer (buf, len), to); LogPrint ("SSU sent ", len, " bytes"); diff --git a/SSU.h b/SSU.h index 16236a38..b0930370 100644 --- a/SSU.h +++ b/SSU.h @@ -2,13 +2,13 @@ #define SSU_H__ #include +#include #include #include #include #include #include -#include -#include +#include "aes.h" #include "I2PEndian.h" #include "Identity.h" #include "RouterInfo.h" @@ -48,15 +48,7 @@ namespace ssu enum SessionState { - eSessionStateUnknown, - eSessionStateRequestSent, - eSessionStateRequestReceived, - eSessionStateCreatedSent, - eSessionStateCreatedReceived, - eSessionStateConfirmedSent, - eSessionStateConfirmedReceived, - eSessionStateRelayRequestSent, - eSessionStateRelayRequestReceived, + eSessionStateUnknown, eSessionStateIntroduced, eSessionStateEstablished, eSessionStateFailed @@ -85,7 +77,7 @@ namespace ssu private: - void CreateAESandMacKey (const uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey); + void CreateAESandMacKey (const uint8_t * pubKey); void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); @@ -106,13 +98,14 @@ namespace ssu void ProcessPeerTest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, uint8_t * introKey); // Charlie to Alice void ProcessData (uint8_t * buf, size_t len); - void SendMsgAck (uint32_t msgID); void SendSesionDestroyed (); - void Send (i2p::I2NPMessage * msg); void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key + void Send (const uint8_t * buf, size_t size); void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey); - void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey); + void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key + void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey); + void DecryptSessionKey (uint8_t * buf, size_t len); bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey); const uint8_t * GetIntroKey () const; @@ -120,7 +113,21 @@ namespace ssu void HandleTerminationTimer (const boost::system::error_code& ecode); private: - + + union IV + { + uint8_t buf[16]; + uint64_t ll[2]; + + IV (const IV&) = default; + IV (const uint8_t * iv) { memcpy (buf, iv, 16); }; + bool operator< (const IV& other) const + { + if (ll[0] != other.ll[0]) return ll[0] < other.ll[0]; + return ll[1] < other.ll[1]; + }; + }; + friend class SSUData; // TODO: change in later SSUServer& m_Server; boost::asio::ip::udp::endpoint m_RemoteEndpoint; @@ -132,10 +139,11 @@ namespace ssu bool m_IsSessionKey; uint32_t m_RelayTag; std::set m_PeerTestNonces; - CryptoPP::CBC_Mode::Encryption m_Encryption; - CryptoPP::CBC_Mode::Decryption m_Decryption; + i2p::crypto::CBCEncryption m_SessionKeyEncryption; + i2p::crypto::CBCDecryption m_SessionKeyDecryption; uint8_t m_SessionKey[32], m_MacKey[32]; std::list m_DelayedMessages; + std::set m_ReceivedIVs; SSUData m_Data; }; @@ -155,7 +163,7 @@ namespace ssu boost::asio::io_service& GetService () { return m_Socket.get_io_service(); }; const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; }; - void Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); + void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay); SSUSession * FindRelaySession (uint32_t tag); diff --git a/SSUData.cpp b/SSUData.cpp index 2f3cbec2..e363a2bf 100644 --- a/SSUData.cpp +++ b/SSUData.cpp @@ -19,8 +19,25 @@ namespace ssu DeleteI2NPMessage (it.second->msg); delete it.second; } + for (auto it: m_SentMessages) + { + for (auto f: it.second) + delete[] f; + } } + void SSUData::ProcessSentMessageAck (uint32_t msgID) + { + auto it = m_SentMessages.find (msgID); + if (it != m_SentMessages.end ()) + { + // delete all ack-ed message's fragments + for (auto f: it->second) + delete[] f; + m_SentMessages.erase (it); + } + } + void SSUData::ProcessMessage (uint8_t * buf, size_t len) { //uint8_t * start = buf; @@ -32,7 +49,8 @@ namespace ssu // explicit ACKs uint8_t numAcks =*buf; buf++; - // TODO: process ACKs + for (int i = 0; i < numAcks; i++) + ProcessSentMessageAck (be32toh (((uint32_t *)buf)[i])); buf += numAcks*4; } if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) @@ -43,7 +61,7 @@ namespace ssu for (int i = 0; i < numBitfields; i++) { buf += 4; // msgID - // TODO: process ACH bitfields + // TODO: process individual Ack bitfields while (*buf & 0x80) // not last buf++; buf++; // last byte @@ -90,6 +108,8 @@ namespace ssu if (isLast) { + if (!msg) + DeleteI2NPMessage (it->second->msg); delete it->second; m_IncomleteMessages.erase (it); } @@ -111,7 +131,7 @@ namespace ssu m_IncomleteMessages[msgID] = new IncompleteMessage (msg); if (isLast) { - m_Session.SendMsgAck (msgID); + SendMsgAck (msgID); msg->FromSSU (msgID); if (m_Session.GetState () == eSessionStateEstablished) i2p::HandleI2NPMessage (msg); @@ -133,6 +153,75 @@ namespace ssu } } + void SSUData::Send (i2p::I2NPMessage * msg) + { + uint32_t msgID = msg->ToSSU (); + auto fragments = m_SentMessages[msgID]; + msgID = htobe32 (msgID); + size_t payloadSize = SSU_MTU - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) + size_t len = msg->GetLength (); + uint8_t * msgBuf = msg->GetSSUHeader (); + + uint32_t fragmentNum = 0; + while (len > 0) + { + uint8_t * buf = new uint8_t[SSU_MTU + 18]; + fragments.push_back (buf); + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = DATA_FLAG_WANT_REPLY; // for compatibility + payload++; + *payload = 1; // always 1 message fragment per message + payload++; + *(uint32_t *)payload = msgID; + payload += 4; + bool isLast = (len <= payloadSize); + size_t size = isLast ? len : payloadSize; + uint32_t fragmentInfo = (fragmentNum << 17); + if (isLast) + fragmentInfo |= 0x010000; + + fragmentInfo |= size; + fragmentInfo = htobe32 (fragmentInfo); + memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3); + payload += 3; + memcpy (payload, msgBuf, size); + + size += payload - buf; + if (size & 0x0F) // make sure 16 bytes boundary + size = ((size >> 4) + 1) << 4; // (/16 + 1)*16 + + // encrypt message with session key + m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size); + m_Session.Send (buf, size); + + if (!isLast) + { + len -= payloadSize; + msgBuf += payloadSize; + } + else + len = 0; + fragmentNum++; + } + DeleteI2NPMessage (msg); + } + + void SSUData::SendMsgAck (uint32_t msgID) + { + uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16 + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag + payload++; + *payload = 1; // number of ACKs + payload++; + *(uint32_t *)(payload) = htobe32 (msgID); // msgID + payload += 4; + *payload = 0; // number of fragments + + // encrypt message with session key + m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); + m_Session.Send (buf, 48); + } } } diff --git a/SSUData.h b/SSUData.h index 6f6c398d..4829aa56 100644 --- a/SSUData.h +++ b/SSUData.h @@ -3,6 +3,7 @@ #include #include +#include #include "I2NPProtocol.h" namespace i2p @@ -27,6 +28,12 @@ namespace ssu ~SSUData (); void ProcessMessage (uint8_t * buf, size_t len); + void Send (i2p::I2NPMessage * msg); + + private: + + void SendMsgAck (uint32_t msgID); + void ProcessSentMessageAck (uint32_t msgID); private: @@ -40,6 +47,7 @@ namespace ssu SSUSession& m_Session; std::map m_IncomleteMessages; + std::map > m_SentMessages; // msgID -> fragments }; } } diff --git a/Streaming.cpp b/Streaming.cpp index d5e3fbcd..7b6ef7c6 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -334,12 +334,13 @@ namespace stream StreamingDestination::StreamingDestination (): m_LeaseSet (nullptr) { m_Keys = i2p::data::CreateRandomKeys (); + m_IdentHash = m_Keys.pub.Hash (); m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, CryptoPP::Integer (m_Keys.signingPrivateKey, 20)); CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey); - m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (*this); + m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (*this, 3); // 3-hops tunnel } StreamingDestination::StreamingDestination (const std::string& fullPath): m_LeaseSet (nullptr) @@ -349,9 +350,13 @@ namespace stream s.read ((char *)&m_Keys, sizeof (m_Keys)); else LogPrint ("Can't open file ", fullPath); + + m_IdentHash = m_Keys.pub.Hash (); + m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, + CryptoPP::Integer (m_Keys.signingPrivateKey, 20)); CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey); - m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (*this); + m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (*this, 3); // 3-hops tunnel } StreamingDestination::~StreamingDestination () diff --git a/TransitTunnel.cpp b/TransitTunnel.cpp index a96f3d05..65b0be4f 100644 --- a/TransitTunnel.cpp +++ b/TransitTunnel.cpp @@ -17,22 +17,12 @@ namespace tunnel m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent), m_NumTransmittedBytes (0) { - memcpy (m_LayerKey, layerKey, 32); - memcpy (m_IVKey, ivKey, 32); + m_Encryption.SetKeys (layerKey, ivKey); } void TransitTunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg) - { - uint8_t * payload = tunnelMsg->GetPayload () + 4; - m_ECBEncryption.SetKey (m_IVKey, 32); - m_ECBEncryption.ProcessData(payload, payload, 16); // iv - - m_CBCEncryption.SetKeyWithIV (m_LayerKey, 32, payload); - m_CBCEncryption.ProcessData(payload + 16, payload + 16, TUNNEL_DATA_ENCRYPTED_SIZE); // payload - - m_ECBEncryption.SetKey (m_IVKey, 32); - m_ECBEncryption.ProcessData(payload, payload, 16); // double iv encryption - + { + m_Encryption.Encrypt (tunnelMsg->GetPayload () + 4); } void TransitTunnel::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg) diff --git a/TransitTunnel.h b/TransitTunnel.h index feeeaa58..ebd7ffd5 100644 --- a/TransitTunnel.h +++ b/TransitTunnel.h @@ -2,8 +2,7 @@ #define TRANSIT_TUNNEL_H__ #include -#include -#include +#include "aes.h" #include "I2NPProtocol.h" #include "TunnelEndpoint.h" #include "TunnelGateway.h" @@ -36,12 +35,9 @@ namespace tunnel uint32_t m_TunnelID, m_NextTunnelID; i2p::data::IdentHash m_NextIdent; - uint8_t m_LayerKey[32]; - uint8_t m_IVKey[32]; size_t m_NumTransmittedBytes; - CryptoPP::ECB_Mode::Encryption m_ECBEncryption; - CryptoPP::CBC_Mode::Encryption m_CBCEncryption; + i2p::crypto::TunnelEncryption m_Encryption; }; class TransitTunnelGateway: public TransitTunnel diff --git a/Tunnel.cpp b/Tunnel.cpp index 4b831fbc..0c1fe454 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -27,7 +27,7 @@ namespace tunnel void Tunnel::Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel) { CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - size_t numRecords = m_Config->GetNumHops (); + int numRecords = m_Config->GetNumHops () + 1; // +1 fake record. TODO: I2NPMessage * msg = NewI2NPMessage (); *msg->GetPayload () = numRecords; msg->len += numRecords*sizeof (I2NPBuildRequestRecordElGamalEncrypted) + 1; @@ -48,21 +48,33 @@ namespace tunnel hop->next ? rnd.GenerateWord32 () : replyMsgID, // we set replyMsgID for last hop only hop->isGateway, hop->isEndpoint), records[i]); + hop->recordIndex = i; //TODO: i++; hop = hop->next; } + // fill up fake records with random data + while (i < numRecords) + { + rnd.GenerateBlock ((uint8_t *)(records + i), sizeof (records[i])); + i++; + } + i2p::crypto::CBCDecryption decryption; hop = m_Config->GetLastHop ()->prev; - size_t ind = numRecords - 1; while (hop) { - for (size_t i = ind; i < numRecords; i++) + decryption.SetKey (hop->replyKey); + // decrypt records after current hop + TunnelHopConfig * hop1 = hop->next; + while (hop1) { - m_CBCDecryption.SetKeyWithIV (hop->replyKey, 32, hop->replyIV); - m_CBCDecryption.ProcessData((uint8_t *)&records[i], (uint8_t *)&records[i], sizeof (I2NPBuildRequestRecordElGamalEncrypted)); + decryption.SetIV (hop->replyIV); + decryption.Decrypt((uint8_t *)&records[hop1->recordIndex], + sizeof (I2NPBuildRequestRecordElGamalEncrypted), + (uint8_t *)&records[hop1->recordIndex]); + hop1 = hop1->next; } hop = hop->prev; - ind--; } FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild); @@ -75,45 +87,53 @@ namespace tunnel bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) { LogPrint ("TunnelBuildResponse ", (int)msg[0], " records."); - + + i2p::crypto::CBCDecryption decryption; TunnelHopConfig * hop = m_Config->GetLastHop (); - int num = msg[0]; while (hop) { - for (int i = 0; i < num; i++) - { - uint8_t * record = msg + 1 + i*sizeof (I2NPBuildResponseRecord); - m_CBCDecryption.SetKeyWithIV(hop->replyKey, 32, hop->replyIV); - m_CBCDecryption.ProcessData(record, record, sizeof (I2NPBuildResponseRecord)); - } + decryption.SetKey (hop->replyKey); + // decrypt records before and including current hop + TunnelHopConfig * hop1 = hop; + while (hop1) + { + auto idx = hop1->recordIndex; + if (idx >= 0 && idx < msg[0]) + { + uint8_t * record = msg + 1 + idx*sizeof (I2NPBuildResponseRecord); + decryption.SetIV (hop->replyIV); + decryption.Decrypt(record, sizeof (I2NPBuildResponseRecord), record); + } + else + LogPrint ("Tunnel hop index ", idx, " is out of range"); + hop1 = hop1->prev; + } hop = hop->prev; - num--; } m_IsEstablished = true; - for (int i = 0; i < msg[0]; i++) + hop = m_Config->GetFirstHop (); + while (hop) { - I2NPBuildResponseRecord * record = (I2NPBuildResponseRecord *)(msg + 1 + i*sizeof (I2NPBuildResponseRecord)); + I2NPBuildResponseRecord * record = (I2NPBuildResponseRecord *)(msg + 1 + hop->recordIndex*sizeof (I2NPBuildResponseRecord)); LogPrint ("Ret code=", (int)record->ret); if (record->ret) // if any of participants declined the tunnel is not established m_IsEstablished = false; + hop = hop->next; } + if (m_IsEstablished) + { + // change reply keys to layer keys + hop = m_Config->GetFirstHop (); + while (hop) + { + hop->decryption.SetKeys (hop->layerKey, hop->ivKey); + hop = hop->next; + } + } return m_IsEstablished; } - - void Tunnel::LayerDecrypt (const uint8_t * in, size_t len, const uint8_t * layerKey, - const uint8_t * iv, uint8_t * out) - { - m_CBCDecryption.SetKeyWithIV (layerKey, 32, iv); - m_CBCDecryption.ProcessData(out, in, len); - } - - void Tunnel::IVDecrypt (const uint8_t * in, const uint8_t * ivKey, uint8_t * out) - { - m_ECBDecryption.SetKey (ivKey, 32); - m_ECBDecryption.ProcessData(out, in, 16); - } void Tunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg) { @@ -121,10 +141,7 @@ namespace tunnel TunnelHopConfig * hop = m_Config->GetLastHop (); while (hop) { - // iv + data - IVDecrypt (payload, hop->ivKey, payload); - LayerDecrypt (payload + 16, TUNNEL_DATA_ENCRYPTED_SIZE, hop->layerKey, payload, payload+16); - IVDecrypt (payload, hop->ivKey, payload); + hop->decryption.Decrypt (payload); hop = hop->prev; } } @@ -258,9 +275,9 @@ namespace tunnel return tunnel; } - TunnelPool * Tunnels::CreateTunnelPool (i2p::data::LocalDestination& localDestination) + TunnelPool * Tunnels::CreateTunnelPool (i2p::data::LocalDestination& localDestination, int numHops) { - auto pool = new TunnelPool (localDestination); + auto pool = new TunnelPool (localDestination, numHops); m_Pools[pool->GetIdentHash ()] = pool; return pool; } @@ -424,7 +441,7 @@ namespace tunnel LogPrint ("Creating zero hops inbound tunnel..."); CreateZeroHopsInboundTunnel (); if (!m_ExploratoryPool) - m_ExploratoryPool = CreateTunnelPool (i2p::context); + m_ExploratoryPool = CreateTunnelPool (i2p::context, 2); // 2-hop exploratory return; } diff --git a/Tunnel.h b/Tunnel.h index 16ffa9e4..8ae3e392 100644 --- a/Tunnel.h +++ b/Tunnel.h @@ -8,8 +8,6 @@ #include #include #include -#include -#include #include "Queue.h" #include "TunnelConfig.h" #include "TunnelPool.h" @@ -51,20 +49,11 @@ namespace tunnel uint32_t GetNextTunnelID () const { return m_Config->GetFirstHop ()->tunnelID; }; const i2p::data::IdentHash& GetNextIdentHash () const { return m_Config->GetFirstHop ()->router->GetIdentHash (); }; - private: - - void LayerDecrypt (const uint8_t * in, size_t len, const uint8_t * layerKey, - const uint8_t * iv, uint8_t * out); - void IVDecrypt (const uint8_t * in, const uint8_t * ivKey, uint8_t * out); - private: TunnelConfig * m_Config; TunnelPool * m_Pool; // pool, tunnel belongs to, or null - bool m_IsEstablished, m_IsFailed; - - CryptoPP::ECB_Mode::Decryption m_ECBDecryption; - CryptoPP::CBC_Mode::Decryption m_CBCDecryption; + bool m_IsEstablished, m_IsFailed; }; class OutboundTunnel: public Tunnel @@ -125,7 +114,7 @@ namespace tunnel void PostTunnelData (I2NPMessage * msg); template TTunnel * CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel = 0); - TunnelPool * CreateTunnelPool (i2p::data::LocalDestination& localDestination); + TunnelPool * CreateTunnelPool (i2p::data::LocalDestination& localDestination, int numHops); void DeleteTunnelPool (TunnelPool * pool); private: diff --git a/TunnelConfig.h b/TunnelConfig.h index b6eccda4..429fa679 100644 --- a/TunnelConfig.h +++ b/TunnelConfig.h @@ -4,6 +4,7 @@ #include #include #include +#include "aes.h" #include "RouterInfo.h" #include "RouterContext.h" @@ -22,7 +23,9 @@ namespace tunnel bool isGateway, isEndpoint; TunnelHopConfig * next, * prev; - + i2p::crypto::TunnelDecryption decryption; + int recordIndex; // record # in tunnel build message + TunnelHopConfig (const i2p::data::RouterInfo * r) { CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); @@ -131,9 +134,9 @@ namespace tunnel return m_LastHop; } - size_t GetNumHops () const + int GetNumHops () const { - size_t num = 0; + int num = 0; TunnelHopConfig * hop = m_FirstHop; while (hop) { @@ -180,6 +183,8 @@ namespace tunnel newConfig->m_LastHop = newHop; if (hop->isGateway) // inbound tunnel newHop->SetReplyHop (m_FirstHop); // use it as reply tunnel + else + newHop->SetNextRouter (&i2p::context.GetRouterInfo ()); } if (!hop->next) newConfig->m_FirstHop = newHop; // last hop diff --git a/TunnelEndpoint.cpp b/TunnelEndpoint.cpp index fc92ec29..cfbc39ec 100644 --- a/TunnelEndpoint.cpp +++ b/TunnelEndpoint.cpp @@ -26,7 +26,8 @@ namespace tunnel bool isFollowOnFragment = flag & 0x80, isLastFragment = true; uint32_t msgID = 0; - TunnelMessageBlock m; + int fragmentNum = 0; + TunnelMessageBlockEx m; if (!isFollowOnFragment) { // first fragment @@ -68,7 +69,7 @@ namespace tunnel // follow on msgID = be32toh (*(uint32_t *)fragment); // MessageID fragment += 4; - int fragmentNum = (flag >> 1) & 0x3F; // 6 bits + fragmentNum = (flag >> 1) & 0x3F; // 6 bits isLastFragment = flag & 0x01; LogPrint ("Follow on fragment ", fragmentNum, " of message ", msgID, isLastFragment ? " last" : " non-last"); } @@ -101,22 +102,34 @@ namespace tunnel if (msgID) // msgID is presented, assume message is fragmented { if (!isFollowOnFragment) // create new incomlete message + { + m.nextFragmentNum = 1; m_IncompleteMessages[msgID] = m; + } else { auto it = m_IncompleteMessages.find (msgID); if (it != m_IncompleteMessages.end()) { - I2NPMessage * incompleteMessage = it->second.data; - memcpy (incompleteMessage->buf + incompleteMessage->len, fragment, size); // concatenate fragment - incompleteMessage->len += size; - // TODO: check fragmentNum sequence - if (isLastFragment) + if (fragmentNum == it->second.nextFragmentNum) { - // message complete - HandleNextMessage (it->second); - m_IncompleteMessages.erase (it); - } + I2NPMessage * incompleteMessage = it->second.data; + memcpy (incompleteMessage->buf + incompleteMessage->len, fragment, size); // concatenate fragment + incompleteMessage->len += size; + if (isLastFragment) + { + // message complete + HandleNextMessage (it->second); + m_IncompleteMessages.erase (it); + } + else + it->second.nextFragmentNum++; + } + else + { + LogPrint ("Unexpected fragment ", fragmentNum, " instead ", it->second.nextFragmentNum, " of message ", msgID, ". Discarded"); + m_IncompleteMessages.erase (it); // TODO: store unexpect fragment for a while + } } else LogPrint ("First fragment of message ", msgID, " not found. Discarded"); diff --git a/TunnelEndpoint.h b/TunnelEndpoint.h index b18f204b..2323e574 100644 --- a/TunnelEndpoint.h +++ b/TunnelEndpoint.h @@ -26,7 +26,12 @@ namespace tunnel private: - std::map m_IncompleteMessages; + struct TunnelMessageBlockEx: public TunnelMessageBlock + { + uint8_t nextFragmentNum; + }; + + std::map m_IncompleteMessages; size_t m_NumReceivedBytes; }; } diff --git a/TunnelPool.cpp b/TunnelPool.cpp index 875a51f6..1b6fa8cb 100644 --- a/TunnelPool.cpp +++ b/TunnelPool.cpp @@ -10,8 +10,8 @@ namespace i2p { namespace tunnel { - TunnelPool::TunnelPool (i2p::data::LocalDestination& localDestination, int numTunnels): - m_LocalDestination (localDestination), m_NumTunnels (numTunnels), m_LastOutboundTunnel (nullptr) + TunnelPool::TunnelPool (i2p::data::LocalDestination& localDestination, int numHops, int numTunnels): + m_LocalDestination (localDestination), m_NumHops (numHops), m_NumTunnels (numTunnels), m_LastOutboundTunnel (nullptr) { } @@ -189,15 +189,18 @@ namespace tunnel if (inboundTunnel) { LogPrint ("Creating destination outbound tunnel..."); - auto firstHop = i2p::data::netdb.GetRandomRouter (&i2p::context.GetRouterInfo ()); - auto secondHop = i2p::data::netdb.GetRandomRouter (firstHop); + + const i2p::data::RouterInfo * prevHop = &i2p::context.GetRouterInfo (); + std::vector hops; + for (int i = 0; i < m_NumHops; i++) + { + auto hop = i2p::data::netdb.GetRandomRouter (prevHop); + prevHop = hop; + hops.push_back (hop); + } + auto * tunnel = tunnels.CreateTunnel ( - new TunnelConfig (std::vector - { - firstHop, - secondHop - }, - inboundTunnel->GetTunnelConfig ())); + new TunnelConfig (hops, inboundTunnel->GetTunnelConfig ())); tunnel->SetTunnelPool (this); } } diff --git a/TunnelPool.h b/TunnelPool.h index 1ff66cf3..27848bfb 100644 --- a/TunnelPool.h +++ b/TunnelPool.h @@ -23,7 +23,7 @@ namespace tunnel { public: - TunnelPool (i2p::data::LocalDestination& localDestination, int numTunnels = 5); + TunnelPool (i2p::data::LocalDestination& localDestination, int numHops, int numTunnels = 5); ~TunnelPool (); const uint8_t * GetEncryptionPrivateKey () const { return m_LocalDestination.GetEncryptionPrivateKey (); }; @@ -53,7 +53,7 @@ namespace tunnel private: i2p::data::LocalDestination& m_LocalDestination; - int m_NumTunnels; + int m_NumHops, m_NumTunnels; std::set m_InboundTunnels; // recent tunnel appears first std::set m_OutboundTunnels; std::map > m_Tests; diff --git a/aes.cpp b/aes.cpp new file mode 100644 index 00000000..518d5c58 --- /dev/null +++ b/aes.cpp @@ -0,0 +1,357 @@ +#include +#include "TunnelBase.h" +#include "aes.h" + +namespace i2p +{ +namespace crypto +{ + +#ifdef AESNI + + ECBCryptoAESNI::ECBCryptoAESNI () + { + m_KeySchedule = m_UnalignedBuffer; + uint8_t rem = ((uint64_t)m_KeySchedule) & 0x0f; + if (rem) + m_KeySchedule += (16 - rem); + } + + #define KeyExpansion256(round0,round1) \ + "pshufd $0xff, %%xmm2, %%xmm2 \n" \ + "movaps %%xmm1, %%xmm4 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pxor %%xmm2, %%xmm1 \n" \ + "movaps %%xmm1, "#round0"(%[sched]) \n" \ + "aeskeygenassist $0, %%xmm1, %%xmm4 \n" \ + "pshufd $0xaa, %%xmm4, %%xmm2 \n" \ + "movaps %%xmm3, %%xmm4 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pxor %%xmm2, %%xmm3 \n" \ + "movaps %%xmm3, "#round1"(%[sched]) \n" + + void ECBCryptoAESNI::ExpandKey (const uint8_t * key) + { + __asm__ + ( + "movups (%[key]), %%xmm1 \n" + "movups 16(%[key]), %%xmm3 \n" + "movaps %%xmm1, (%[sched]) \n" + "movaps %%xmm3, 16(%[sched]) \n" + "aeskeygenassist $1, %%xmm3, %%xmm2 \n" + KeyExpansion256(32,48) + "aeskeygenassist $2, %%xmm3, %%xmm2 \n" + KeyExpansion256(64,80) + "aeskeygenassist $4, %%xmm3, %%xmm2 \n" + KeyExpansion256(96,112) + "aeskeygenassist $8, %%xmm3, %%xmm2 \n" + KeyExpansion256(128,144) + "aeskeygenassist $16, %%xmm3, %%xmm2 \n" + KeyExpansion256(160,176) + "aeskeygenassist $32, %%xmm3, %%xmm2 \n" + KeyExpansion256(192,208) + "aeskeygenassist $64, %%xmm3, %%xmm2 \n" + // key expansion final + "pshufd $0xff, %%xmm2, %%xmm2 \n" + "movaps %%xmm1, %%xmm4 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pxor %%xmm2, %%xmm1 \n" + "movups %%xmm1, 224(%[sched]) \n" + : // output + : [key]"r"(key), [sched]"r"(m_KeySchedule) // input + : "%xmm1", "%xmm2", "%xmm3", "%xmm4" // clogged + ); + } + + #define EncryptAES256(sched) \ + "pxor (%["#sched"]), %%xmm0 \n" \ + "aesenc 16(%["#sched"]), %%xmm0 \n" \ + "aesenc 32(%["#sched"]), %%xmm0 \n" \ + "aesenc 48(%["#sched"]), %%xmm0 \n" \ + "aesenc 64(%["#sched"]), %%xmm0 \n" \ + "aesenc 80(%["#sched"]), %%xmm0 \n" \ + "aesenc 96(%["#sched"]), %%xmm0 \n" \ + "aesenc 112(%["#sched"]), %%xmm0 \n" \ + "aesenc 128(%["#sched"]), %%xmm0 \n" \ + "aesenc 144(%["#sched"]), %%xmm0 \n" \ + "aesenc 160(%["#sched"]), %%xmm0 \n" \ + "aesenc 176(%["#sched"]), %%xmm0 \n" \ + "aesenc 192(%["#sched"]), %%xmm0 \n" \ + "aesenc 208(%["#sched"]), %%xmm0 \n" \ + "aesenclast 224(%["#sched"]), %%xmm0 \n" + + void ECBEncryptionAESNI::Encrypt (const ChipherBlock * in, ChipherBlock * out) + { + __asm__ + ( + "movups (%[in]), %%xmm0 \n" + EncryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + : : [sched]"r"(m_KeySchedule), [in]"r"(in), [out]"r"(out) : "%xmm0" + ); + } + + #define DecryptAES256(sched) \ + "pxor 224(%["#sched"]), %%xmm0 \n" \ + "aesdec 208(%["#sched"]), %%xmm0 \n" \ + "aesdec 192(%["#sched"]), %%xmm0 \n" \ + "aesdec 176(%["#sched"]), %%xmm0 \n" \ + "aesdec 160(%["#sched"]), %%xmm0 \n" \ + "aesdec 144(%["#sched"]), %%xmm0 \n" \ + "aesdec 128(%["#sched"]), %%xmm0 \n" \ + "aesdec 112(%["#sched"]), %%xmm0 \n" \ + "aesdec 96(%["#sched"]), %%xmm0 \n" \ + "aesdec 80(%["#sched"]), %%xmm0 \n" \ + "aesdec 64(%["#sched"]), %%xmm0 \n" \ + "aesdec 48(%["#sched"]), %%xmm0 \n" \ + "aesdec 32(%["#sched"]), %%xmm0 \n" \ + "aesdec 16(%["#sched"]), %%xmm0 \n" \ + "aesdeclast (%["#sched"]), %%xmm0 \n" + + void ECBDecryptionAESNI::Decrypt (const ChipherBlock * in, ChipherBlock * out) + { + __asm__ + ( + "movups (%[in]), %%xmm0 \n" + DecryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + : : [sched]"r"(m_KeySchedule), [in]"r"(in), [out]"r"(out) : "%xmm0" + ); + } + + #define CallAESIMC(offset) \ + "movaps "#offset"(%[shed]), %%xmm0 \n" \ + "aesimc %%xmm0, %%xmm0 \n" \ + "movaps %%xmm0, "#offset"(%[shed]) \n" + + void ECBDecryptionAESNI::SetKey (const uint8_t * key) + { + ExpandKey (key); // expand encryption key first + // then invert it using aesimc + __asm__ + ( + CallAESIMC(16) + CallAESIMC(32) + CallAESIMC(48) + CallAESIMC(64) + CallAESIMC(80) + CallAESIMC(96) + CallAESIMC(112) + CallAESIMC(128) + CallAESIMC(144) + CallAESIMC(160) + CallAESIMC(176) + CallAESIMC(192) + CallAESIMC(208) + : : [shed]"r"(m_KeySchedule) : "%xmm0" + ); + } + +#endif + + + void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) + { +#ifdef AESNI + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "block_e: \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched) + "movaps %%xmm0, %%xmm1 \n" + "movups %%xmm0, (%[out]) \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "dec %[num] \n" + "jnz block_e; \n" + "movups %%xmm1, (%[iv]) \n" + : + : [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) + : "%xmm0", "%xmm1", "cc", "memory" + ); +#else + for (int i = 0; i < numBlocks; i++) + { + m_LastBlock ^= in[i]; + m_ECBEncryption.Encrypt (&m_LastBlock, &m_LastBlock); + out[i] = m_LastBlock; + } +#endif + } + + void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out) + { + // len/16 + Encrypt (len >> 4, (const ChipherBlock *)in, (ChipherBlock *)out); + } + + void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out) + { +#ifdef AESNI + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + "movups %%xmm0, (%[iv]) \n" + : + : [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out) + : "%xmm0", "%xmm1", "memory" + ); +#else + Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); +#endif + } + + void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) + { +#ifdef AESNI + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "block_d: \n" + "movups (%[in]), %%xmm0 \n" + "movaps %%xmm0, %%xmm2 \n" + DecryptAES256(sched) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + "movaps %%xmm2, %%xmm1 \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "dec %[num] \n" + "jnz block_d; \n" + "movups %%xmm1, (%[iv]) \n" + : + : [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) + : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" + ); +#else + for (int i = 0; i < numBlocks; i++) + { + ChipherBlock tmp = in[i]; + m_ECBDecryption.Decrypt (in + i, out + i); + out[i] ^= m_IV; + m_IV = tmp; + } +#endif + } + + void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out) + { + Decrypt (len >> 4, (const ChipherBlock *)in, (ChipherBlock *)out); + } + + void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out) + { +#ifdef AESNI + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "movups (%[in]), %%xmm0 \n" + "movups %%xmm0, (%[iv]) \n" + DecryptAES256(sched) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + : + : [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out) + : "%xmm0", "%xmm1", "memory" + ); +#else + Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); +#endif + } + + void TunnelEncryption::Encrypt (uint8_t * payload) + { +#ifdef AESNI + __asm__ + ( + // encrypt IV + "movups (%[payload]), %%xmm0 \n" + EncryptAES256(sched_iv) + "movaps %%xmm0, %%xmm1 \n" + // double IV encryption + EncryptAES256(sched_iv) + "movups %%xmm0, (%[payload]) \n" + // encrypt data, IV is xmm1 + "block_et: \n" + "add $16, %[payload] \n" + "movups (%[payload]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched_l) + "movaps %%xmm0, %%xmm1 \n" + "movups %%xmm0, (%[payload]) \n" + "dec %[num] \n" + "jnz block_et; \n" + : + : [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.GetKeySchedule ()), + [payload]"r"(payload), [num]"r"(63) // 63 blocks = 1008 bytes + : "%xmm0", "%xmm1", "cc", "memory" + ); +#else + m_IVEncryption.Encrypt ((ChipherBlock *)payload, (ChipherBlock *)payload); // iv + m_LayerEncryption.Encrypt (payload + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, payload + 16); // data + m_IVEncryption.Encrypt ((ChipherBlock *)payload, (ChipherBlock *)payload); // double iv +#endif + } + + void TunnelDecryption::Decrypt (uint8_t * payload) + { +#ifdef AESNI + __asm__ + ( + // decrypt IV + "movups (%[payload]), %%xmm0 \n" + DecryptAES256(sched_iv) + "movaps %%xmm0, %%xmm1 \n" + // double IV encryption + DecryptAES256(sched_iv) + "movups %%xmm0, (%[payload]) \n" + // decrypt data, IV is xmm1 + "block_dt: \n" + "add $16, %[payload] \n" + "movups (%[payload]), %%xmm0 \n" + "movaps %%xmm0, %%xmm2 \n" + DecryptAES256(sched_l) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[payload]) \n" + "movaps %%xmm2, %%xmm1 \n" + "dec %[num] \n" + "jnz block_dt; \n" + : + : [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.GetKeySchedule ()), + [payload]"r"(payload), [num]"r"(63) // 63 blocks = 1008 bytes + : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" + ); +#else + m_IVDecryption.Decrypt ((ChipherBlock *)payload, (ChipherBlock *)payload); // iv + m_LayerDecryption.Decrypt (payload + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, payload + 16); // data + m_IVDecryption.Decrypt ((ChipherBlock *)payload, (ChipherBlock *)payload); // double iv +#endif + } +} +} + diff --git a/aes.h b/aes.h new file mode 100644 index 00000000..d1f3c254 --- /dev/null +++ b/aes.h @@ -0,0 +1,188 @@ +#ifndef AES_H__ +#define AES_H__ + +#include +#include +#include + +namespace i2p +{ +namespace crypto +{ + union ChipherBlock + { + uint8_t buf[16]; + uint64_t ll[2]; + + void operator^=(const ChipherBlock& other) // XOR + { + ll[0] ^= other.ll[0]; + ll[1] ^= other.ll[1]; + } + }; + +#ifdef AESNI + class ECBCryptoAESNI + { + public: + + ECBCryptoAESNI (); + uint8_t * GetKeySchedule () { return m_KeySchedule; }; + + protected: + + void ExpandKey (const uint8_t * key); + + protected: + + uint8_t * m_KeySchedule; // start of 16 bytes boundary of m_UnalignedBuffer + uint8_t m_UnalignedBuffer[256]; // 14 rounds for AES-256, 240 + 16 bytes + }; + + class ECBEncryptionAESNI: public ECBCryptoAESNI + { + public: + + void SetKey (const uint8_t * key) { ExpandKey (key); }; + void Encrypt (const ChipherBlock * in, ChipherBlock * out); + }; + + class ECBDecryptionAESNI: public ECBCryptoAESNI + { + public: + + void SetKey (const uint8_t * key); + void Decrypt (const ChipherBlock * in, ChipherBlock * out); + }; + + typedef ECBEncryptionAESNI ECBEncryption; + typedef ECBDecryptionAESNI ECBDecryption; + +#else // use crypto++ + + class ECBEncryption + { + public: + + void SetKey (const uint8_t * key) + { + m_Encryption.SetKey (key, 32); + } + void Encrypt (const ChipherBlock * in, ChipherBlock * out) + { + m_Encryption.ProcessData (out->buf, in->buf, 16); + } + + private: + + CryptoPP::ECB_Mode::Encryption m_Encryption; + }; + + class ECBDecryption + { + public: + + void SetKey (const uint8_t * key) + { + m_Decryption.SetKey (key, 32); + } + void Decrypt (const ChipherBlock * in, ChipherBlock * out) + { + m_Decryption.ProcessData (out->buf, in->buf, 16); + } + + private: + + CryptoPP::ECB_Mode::Decryption m_Decryption; + }; + + +#endif + + class CBCEncryption + { + public: + + CBCEncryption () { memset (m_LastBlock.buf, 0, 16); }; + + void SetKey (const uint8_t * key) { m_ECBEncryption.SetKey (key); }; // 32 bytes + void SetIV (const uint8_t * iv) { memcpy (m_LastBlock.buf, iv, 16); }; // 16 bytes + + void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); + void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out); + void Encrypt (const uint8_t * in, uint8_t * out); // one block + + private: + + ChipherBlock m_LastBlock; + + ECBEncryption m_ECBEncryption; + }; + + class CBCDecryption + { + public: + + CBCDecryption () { memset (m_IV.buf, 0, 16); }; + + void SetKey (const uint8_t * key) { m_ECBDecryption.SetKey (key); }; // 32 bytes + void SetIV (const uint8_t * iv) { memcpy (m_IV.buf, iv, 16); }; // 16 bytes + + void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); + void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out); + void Decrypt (const uint8_t * in, uint8_t * out); // one block + + private: + + ChipherBlock m_IV; + ECBDecryption m_ECBDecryption; + }; + + class TunnelEncryption // with double IV encryption + { + public: + + void SetKeys (const uint8_t * layerKey, const uint8_t * ivKey) + { + m_LayerEncryption.SetKey (layerKey); + m_IVEncryption.SetKey (ivKey); + } + + void Encrypt (uint8_t * payload); // 1024 bytes (16 IV + 1008 data) + + private: + + ECBEncryption m_IVEncryption; +#ifdef AESNI + ECBEncryption m_LayerEncryption; +#else + CBCEncryption m_LayerEncryption; +#endif + }; + + class TunnelDecryption // with double IV encryption + { + public: + + void SetKeys (const uint8_t * layerKey, const uint8_t * ivKey) + { + m_LayerDecryption.SetKey (layerKey); + m_IVDecryption.SetKey (ivKey); + } + + void Decrypt (uint8_t * payload); // 1024 bytes (16 IV + 1008 data) + + private: + + ECBDecryption m_IVDecryption; +#ifdef AESNI + ECBDecryption m_LayerDecryption; +#else + CBCDecryption m_LayerDecryption; +#endif + }; +} +} + +#endif + diff --git a/hmac.h b/hmac.h index 971b9f1a..9038cf49 100644 --- a/hmac.h +++ b/hmac.h @@ -18,38 +18,38 @@ namespace crypto // digest is 16 bytes // block size is 64 bytes { - uint8_t buf[2048]; + uint64_t buf[256]; // ikeypad - ((uint64_t *)buf)[0] = ((uint64_t *)key)[0] ^ IPAD; - ((uint64_t *)buf)[1] = ((uint64_t *)key)[1] ^ IPAD; - ((uint64_t *)buf)[2] = ((uint64_t *)key)[2] ^ IPAD; - ((uint64_t *)buf)[3] = ((uint64_t *)key)[3] ^ IPAD; - ((uint64_t *)buf)[4] = IPAD; - ((uint64_t *)buf)[5] = IPAD; - ((uint64_t *)buf)[6] = IPAD; - ((uint64_t *)buf)[7] = IPAD; + buf[0] = ((uint64_t *)key)[0] ^ IPAD; + buf[1] = ((uint64_t *)key)[1] ^ IPAD; + buf[2] = ((uint64_t *)key)[2] ^ IPAD; + buf[3] = ((uint64_t *)key)[3] ^ IPAD; + buf[4] = IPAD; + buf[5] = IPAD; + buf[6] = IPAD; + buf[7] = IPAD; // concatenate with msg - memcpy (buf + 64, msg, len); + memcpy (buf + 8, msg, len); // calculate first hash uint8_t hash[16]; // MD5 - CryptoPP::Weak1::MD5().CalculateDigest (hash, buf, len + 64); + CryptoPP::Weak1::MD5().CalculateDigest (hash, (uint8_t *)buf, len + 64); // okeypad - ((uint64_t *)buf)[0] = ((uint64_t *)key)[0] ^ OPAD; - ((uint64_t *)buf)[1] = ((uint64_t *)key)[1] ^ OPAD; - ((uint64_t *)buf)[2] = ((uint64_t *)key)[2] ^ OPAD; - ((uint64_t *)buf)[3] = ((uint64_t *)key)[3] ^ OPAD; - ((uint64_t *)buf)[4] = OPAD; - ((uint64_t *)buf)[5] = OPAD; - ((uint64_t *)buf)[6] = OPAD; - ((uint64_t *)buf)[7] = OPAD; + buf[0] = ((uint64_t *)key)[0] ^ OPAD; + buf[1] = ((uint64_t *)key)[1] ^ OPAD; + buf[2] = ((uint64_t *)key)[2] ^ OPAD; + buf[3] = ((uint64_t *)key)[3] ^ OPAD; + buf[4] = OPAD; + buf[5] = OPAD; + buf[6] = OPAD; + buf[7] = OPAD; // copy first hash after okeypad - memcpy (buf + 64, hash, 16); + memcpy (buf + 8, hash, 16); // fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P) - memset (buf + 80, 0, 16); + memset (buf + 10, 0, 16); // calculate digest - CryptoPP::Weak1::MD5().CalculateDigest (digest, buf, 96); + CryptoPP::Weak1::MD5().CalculateDigest (digest, (uint8_t *)buf, 96); } } } diff --git a/i2p.cpp b/i2p.cpp index f441fc02..2bd2b5b8 100644 --- a/i2p.cpp +++ b/i2p.cpp @@ -1,5 +1,5 @@ #include - +#include #include "Daemon.h" int main( int argc, char* argv[] )