Browse Source

Merge pull request #5 from orignal/master

Merge pull request from orignal/master
pull/77/head
chertov 10 years ago
parent
commit
68a08c2237
  1. 66
      CryptoConst.cpp
  2. 64
      CryptoConst.h
  3. 9
      Daemon.cpp
  4. 4
      Daemon.h
  5. 32
      Garlic.cpp
  6. 12
      Garlic.h
  7. 7
      HTTPServer.cpp
  8. 119
      I2NPProtocol.cpp
  9. 10
      I2NPProtocol.h
  10. 25
      I2PEndian.h
  11. 25
      Identity.cpp
  12. 19
      Identity.h
  13. 23
      Log.cpp
  14. 14
      Log.h
  15. 13
      Makefile
  16. 43
      Makefile.osx
  17. 30
      NTCPSession.cpp
  18. 5
      NTCPSession.h
  19. 74
      NetDb.cpp
  20. 10
      NetDb.h
  21. 5
      RouterInfo.cpp
  22. 5
      RouterInfo.h
  23. 209
      SSU.cpp
  24. 46
      SSU.h
  25. 95
      SSUData.cpp
  26. 8
      SSUData.h
  27. 9
      Streaming.cpp
  28. 16
      TransitTunnel.cpp
  29. 8
      TransitTunnel.h
  30. 91
      Tunnel.cpp
  31. 15
      Tunnel.h
  32. 11
      TunnelConfig.h
  33. 35
      TunnelEndpoint.cpp
  34. 7
      TunnelEndpoint.h
  35. 23
      TunnelPool.cpp
  36. 4
      TunnelPool.h
  37. 357
      aes.cpp
  38. 188
      aes.h
  39. 44
      hmac.h
  40. 2
      i2p.cpp

66
CryptoConst.cpp

@ -0,0 +1,66 @@
#include <inttypes.h>
#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);
}
}

64
CryptoConst.h

@ -7,67 +7,15 @@ namespace i2p
{ {
namespace crypto namespace crypto
{ {
// DH // DH
extern const CryptoPP::Integer elgp;
inline const CryptoPP::Integer& get_elgp () extern const CryptoPP::Integer elgg;
{
static const CryptoPP::Integer elgp_ (
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
"83655D23DCA3AD961C62F356208552BB9ED529077096966D"
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
"15728E5A8AACAA68FFFFFFFFFFFFFFFF"
"h");
return elgp_;
}
#define elgp get_elgp()
inline const CryptoPP::Integer& get_elgg ()
{
static const CryptoPP::Integer elgg_ (2);
return elgg_;
}
#define elgg get_elgg()
// DSA // DSA
inline const CryptoPP::Integer& get_dsap () extern const CryptoPP::Integer dsap;
{ extern const CryptoPP::Integer dsaq;
static const CryptoPP::Integer dsap_ ( extern const CryptoPP::Integer dsag;
"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()
} }
} }

9
Daemon.cpp

@ -53,7 +53,7 @@ namespace i2p
//TODO: This is an ugly workaround. fix it. //TODO: This is an ugly workaround. fix it.
//TODO: Autodetect public IP. //TODO: Autodetect public IP.
i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"), 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) if (isLogging == 1)
{ {
@ -63,12 +63,7 @@ namespace i2p
#else #else
logfile_path.append("\\debug.log"); logfile_path.append("\\debug.log");
#endif #endif
logfile.open(logfile_path, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); g_Log.SetLogFile (logfile_path);
if (!logfile.is_open())
exit(-17);
LogPrint("Logging to file enabled.");
LogPrint("CMD parameters:"); LogPrint("CMD parameters:");
for (int i = 0; i < argc; ++i) for (int i = 0; i < argc; ++i)

4
Daemon.h

@ -1,5 +1,5 @@
#pragma once #pragma once
#include <fstream> #include <string>
#ifdef _WIN32 #ifdef _WIN32
#define Daemon i2p::util::DaemonWin32::Instance() #define Daemon i2p::util::DaemonWin32::Instance()
@ -24,8 +24,6 @@ namespace i2p
int running; int running;
std::ofstream logfile;
protected: protected:
Daemon_Singleton(); Daemon_Singleton();
virtual ~Daemon_Singleton(); virtual ~Daemon_Singleton();

32
Garlic.cpp

@ -20,6 +20,7 @@ namespace garlic
{ {
// create new session tags and session key // create new session tags and session key
m_Rnd.GenerateBlock (m_SessionKey, 32); m_Rnd.GenerateBlock (m_SessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
if (m_NumTags > 0) if (m_NumTags > 0)
{ {
m_SessionTags = new uint8_t[m_NumTags*32]; m_SessionTags = new uint8_t[m_NumTags*32];
@ -77,7 +78,7 @@ namespace garlic
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
m_Destination.GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true); m_Destination.GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); m_Encryption.SetIV (iv);
buf += 514; buf += 514;
len += 514; len += 514;
} }
@ -87,7 +88,7 @@ namespace garlic
memcpy (buf, m_SessionTags + m_NextTag*32, 32); memcpy (buf, m_SessionTags + m_NextTag*32, 32);
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, m_SessionTags + m_NextTag*32, 32); CryptoPP::SHA256().CalculateDigest(iv, m_SessionTags + m_NextTag*32, 32);
m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); m_Encryption.SetIV (iv);
buf += 32; buf += 32;
len += 32; len += 32;
@ -132,7 +133,7 @@ namespace garlic
size_t rem = blockSize % 16; size_t rem = blockSize % 16;
if (rem) if (rem)
blockSize += (16-rem); //padding blockSize += (16-rem); //padding
m_Encryption.ProcessData(buf, buf, blockSize); m_Encryption.Encrypt(buf, blockSize, buf);
return blockSize; return blockSize;
} }
@ -248,6 +249,9 @@ namespace garlic
for (auto it: m_Sessions) for (auto it: m_Sessions)
delete it.second; delete it.second;
m_Sessions.clear (); m_Sessions.clear ();
for (auto it: m_SessionDecryptions)
delete it;
m_SessionDecryptions.clear ();
} }
I2NPMessage * GarlicRouting::WrapSingleMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg) I2NPMessage * GarlicRouting::WrapSingleMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg)
@ -298,13 +302,12 @@ namespace garlic
if (it != m_SessionTags.end ()) if (it != m_SessionTags.end ())
{ {
// existing session // 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 uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, buf, 32); CryptoPP::SHA256().CalculateDigest(iv, buf, 32);
m_Decryption.SetKeyWithIV ((uint8_t *)sessionKey.c_str (), 32, iv); // tag is mapped to 32 bytes key it->second->SetIV (iv);
m_Decryption.ProcessData(buf + 32, buf + 32, length - 32); it->second->Decrypt (buf + 32, length - 32, buf + 32);
HandleAESBlock (buf + 32, length - 32, (uint8_t *)sessionKey.c_str ()); HandleAESBlock (buf + 32, length - 32, it->second);
m_SessionTags.erase (it); // tag might be used only once
} }
else else
{ {
@ -317,11 +320,14 @@ namespace garlic
pool ? pool->GetEncryptionPrivateKey () : i2p::context.GetPrivateKey (), pool ? pool->GetEncryptionPrivateKey () : i2p::context.GetPrivateKey (),
buf, (uint8_t *)&elGamal, true)) 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 uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
m_Decryption.SetKeyWithIV (elGamal.sessionKey, 32, iv); decryption->SetIV (iv);
m_Decryption.ProcessData(buf + 514, buf + 514, length - 514); decryption->Decrypt(buf + 514, length - 514, buf + 514);
HandleAESBlock (buf + 514, length - 514, elGamal.sessionKey); HandleAESBlock (buf + 514, length - 514, decryption);
} }
else else
LogPrint ("Failed to decrypt garlic"); LogPrint ("Failed to decrypt garlic");
@ -329,12 +335,12 @@ namespace garlic
DeleteI2NPMessage (msg); 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); uint16_t tagCount = be16toh (*(uint16_t *)buf);
buf += 2; buf += 2;
for (int i = 0; i < tagCount; i++) 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; buf += tagCount*32;
uint32_t payloadSize = be32toh (*(uint32_t *)buf); uint32_t payloadSize = be32toh (*(uint32_t *)buf);
if (payloadSize > len) if (payloadSize > len)

12
Garlic.h

@ -3,11 +3,11 @@
#include <inttypes.h> #include <inttypes.h>
#include <map> #include <map>
#include <list>
#include <string> #include <string>
#include <thread> #include <thread>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
#include <cryptopp/osrng.h> #include <cryptopp/osrng.h>
#include "aes.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "LeaseSet.h" #include "LeaseSet.h"
#include "Tunnel.h" #include "Tunnel.h"
@ -68,7 +68,7 @@ namespace garlic
uint8_t * m_SessionTags; // m_NumTags*32 bytes uint8_t * m_SessionTags; // m_NumTags*32 bytes
uint32_t m_TagsCreationTime; // seconds since epoch uint32_t m_TagsCreationTime; // seconds since epoch
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption; i2p::crypto::CBCEncryption m_Encryption;
CryptoPP::AutoSeededRandomPool m_Rnd; CryptoPP::AutoSeededRandomPool m_Rnd;
}; };
@ -93,7 +93,7 @@ namespace garlic
void Run (); void Run ();
void ProcessGarlicMessage (I2NPMessage * msg); 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); void HandleGarlicPayload (uint8_t * buf, size_t len);
private: private:
@ -105,8 +105,8 @@ namespace garlic
std::map<i2p::data::IdentHash, GarlicRoutingSession *> m_Sessions; std::map<i2p::data::IdentHash, GarlicRoutingSession *> m_Sessions;
std::map<uint32_t, GarlicRoutingSession *> m_CreatedSessions; // msgID -> session std::map<uint32_t, GarlicRoutingSession *> m_CreatedSessions; // msgID -> session
// incoming session // incoming session
std::map<std::string, std::string> m_SessionTags; // tag -> key std::list<i2p::crypto::CBCDecryption *> m_SessionDecryptions; // multiple tags refer to one decyption
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption; std::map<std::string, i2p::crypto::CBCDecryption *> m_SessionTags; // tag -> decryption
}; };
extern GarlicRouting routing; extern GarlicRouting routing;

7
HTTPServer.cpp

@ -238,7 +238,7 @@ namespace util
void HTTPConnection::FillContent (std::stringstream& s) void HTTPConnection::FillContent (std::stringstream& s)
{ {
s << "Data path: " << i2p::util::filesystem::GetDataDir().string() << "<BR>" << "<BR>"; s << "Data path: " << i2p::util::filesystem::GetDataDir().string() << "<BR>" << "<BR>";
s << "Our external address:" << "<BR>" << "<BR>"; s << "Our external address:" << "<BR>";
for (auto& address : i2p::context.GetRouterInfo().GetAddresses()) for (auto& address : i2p::context.GetRouterInfo().GetAddresses())
{ {
switch (address.transportStyle) switch (address.transportStyle)
@ -254,7 +254,10 @@ namespace util
} }
s << address.host.to_string() << ":" << address.port << "<BR>"; s << address.host.to_string() << ":" << address.port << "<BR>";
} }
s << "<BR>Routers: " << i2p::data::netdb.GetNumRouters () << " ";
s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << " ";
s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "<BR>";
s << "<P>Tunnels</P>"; s << "<P>Tunnels</P>";
for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ()) for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ())
{ {

119
I2NPProtocol.cpp

@ -1,8 +1,6 @@
#include <string.h> #include <string.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include <cryptopp/sha.h> #include <cryptopp/sha.h>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
#include <cryptopp/gzip.h> #include <cryptopp/gzip.h>
#include "ElGamal.h" #include "ElGamal.h"
#include "Timestamp.h" #include "Timestamp.h"
@ -236,6 +234,42 @@ namespace i2p
memcpy (record.toPeer, (const uint8_t *)router.GetIdentHash (), 16); 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) void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{ {
int num = buf[0]; int num = buf[0];
@ -260,52 +294,43 @@ namespace i2p
else else
{ {
I2NPBuildRequestRecordElGamalEncrypted * records = (I2NPBuildRequestRecordElGamalEncrypted *)(buf+1); I2NPBuildRequestRecordElGamalEncrypted * records = (I2NPBuildRequestRecordElGamalEncrypted *)(buf+1);
for (int i = 0; i < num; i++) I2NPBuildRequestRecordClearText clearText;
{ if (HandleBuildRequestRecords (num, records, clearText))
if (!memcmp (records[i].toPeer, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) {
{ if (clearText.flag & 0x40) // we are endpoint of outboud tunnel
LogPrint ("Record ",i," is ours"); {
// so we send it to reply tunnel
I2NPBuildRequestRecordClearText clearText; i2p::transports.SendMessage (clearText.nextIdent,
i2p::crypto::ElGamalDecrypt (i2p::context.GetPrivateKey (), records[i].encrypted, (uint8_t *)&clearText); CreateTunnelGatewayMsg (be32toh (clearText.nextTunnel),
eI2NPVariableTunnelBuildReply, buf, len,
i2p::tunnel::TransitTunnel * transitTunnel = be32toh (clearText.nextMessageID)));
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<CryptoPP::AES>::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;
} }
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) void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{ {
LogPrint ("VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID); LogPrint ("VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID);
@ -444,6 +469,14 @@ namespace i2p
LogPrint ("VariableTunnelBuildReply"); LogPrint ("VariableTunnelBuildReply");
HandleVariableTunnelBuildReplyMsg (msgID, buf, size); HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
break; break;
case eI2NPTunnelBuild:
LogPrint ("TunnelBuild");
HandleTunnelBuildMsg (buf, size);
break;
case eI2NPTunnelBuildReply:
LogPrint ("TunnelBuildReply");
// TODO:
break;
case eI2NPDatabaseLookup: case eI2NPDatabaseLookup:
LogPrint ("DatabaseLookup"); LogPrint ("DatabaseLookup");
HandleDatabaseLookupMsg (buf, size); HandleDatabaseLookupMsg (buf, size);

10
I2NPProtocol.h

@ -87,10 +87,14 @@ namespace i2p
eI2NPTunnelData = 18, eI2NPTunnelData = 18,
eI2NPTunnelGateway = 19, eI2NPTunnelGateway = 19,
eI2NPData = 20, eI2NPData = 20,
eI2NPTunnelBuild = 21,
eI2NPTunnelBuildReply = 22,
eI2NPVariableTunnelBuild = 23, eI2NPVariableTunnelBuild = 23,
eI2NPVariableTunnelBuildReply = 24 eI2NPVariableTunnelBuildReply = 24
}; };
const int NUM_TUNNEL_BUILD_RECORDS = 8;
namespace tunnel namespace tunnel
{ {
class InboundTunnel; class InboundTunnel;
@ -164,10 +168,12 @@ namespace tunnel
void EncryptBuildRequestRecord (const i2p::data::RouterInfo& router, void EncryptBuildRequestRecord (const i2p::data::RouterInfo& router,
const I2NPBuildRequestRecordClearText& clearText, const I2NPBuildRequestRecordClearText& clearText,
I2NPBuildRequestRecordElGamalEncrypted& record); I2NPBuildRequestRecordElGamalEncrypted& record);
bool HandleBuildRequestRecords (int num, I2NPBuildRequestRecordElGamalEncrypted * records, I2NPBuildRequestRecordClearText& clearText);
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
void HandleVariableTunnelBuildReplyMsg (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 (const uint8_t * buf);
I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload); I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);

25
I2PEndian.h

@ -5,11 +5,27 @@
#include <endian.h> #include <endian.h>
#elif __FreeBSD__ #elif __FreeBSD__
#include <sys/endian.h> #include <sys/endian.h>
#elif __MACH__ // Mac OS X #elif defined(__APPLE__) && defined(__MACH__)
#include <machine/endian.h>
#include <libkern/OSByteOrder.h>
#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 #else
#include <cstdint> #include <cstdint>
uint16_t htobe16(uint16_t int16); uint16_t htobe16(uint16_t int16);
uint32_t htobe32(uint32_t int32); uint32_t htobe32(uint32_t int32);
uint64_t htobe64(uint64_t int64); uint64_t htobe64(uint64_t int64);
@ -20,4 +36,5 @@ uint64_t be64toh(uint64_t big64);
#endif #endif
#endif // I2PENDIAN_H__ #endif // I2PENDIAN_H__

25
Identity.cpp

@ -22,14 +22,14 @@ namespace data
bool Identity::FromBase64 (const std::string& s) bool Identity::FromBase64 (const std::string& s)
{ {
size_t count = Base64ToByteStream (s.c_str(), s.length(), reinterpret_cast<uint8_t*> (this), sizeof (Identity)); size_t count = Base64ToByteStream (s.c_str(), s.length(), publicKey, sizeof (Identity));
return count == sizeof(Identity); return count == sizeof(Identity);
} }
IdentHash Identity::Hash() IdentHash Identity::Hash() const
{ {
IdentHash hash; IdentHash hash;
CryptoPP::SHA256().CalculateDigest(reinterpret_cast<uint8_t*>(&hash), reinterpret_cast<uint8_t*> (this), sizeof (Identity)); CryptoPP::SHA256().CalculateDigest(hash, publicKey, sizeof (Identity));
return hash; return hash;
} }
@ -40,11 +40,11 @@ namespace data
return *this; return *this;
} }
bool IdentHash::FromBase32(const std::string& s) bool IdentHash::FromBase32(const std::string& s)
{ {
size_t count = Base32ToByteStream(s.c_str(), s.length(), m_Hash, sizeof(m_Hash)); size_t count = Base32ToByteStream(s.c_str(), s.length(), m_Hash, sizeof(m_Hash));
return count == sizeof(m_Hash); return count == sizeof(m_Hash);
} }
Keys CreateRandomKeys () Keys CreateRandomKeys ()
{ {
@ -98,12 +98,11 @@ namespace data
XORMetric operator^(const RoutingKey& key1, const RoutingKey& key2) XORMetric operator^(const RoutingKey& key1, const RoutingKey& key2)
{ {
// TODO: implementation depends on CPU
XORMetric m; XORMetric m;
((uint64_t *)m.metric)[0] = ((uint64_t *)key1.hash)[0] ^ ((uint64_t *)key2.hash)[0]; m.metric_ll[0] = key1.hash_ll[0] ^ key2.hash_ll[0];
((uint64_t *)m.metric)[1] = ((uint64_t *)key1.hash)[1] ^ ((uint64_t *)key2.hash)[1]; m.metric_ll[1] = key1.hash_ll[1] ^ key2.hash_ll[1];
((uint64_t *)m.metric)[2] = ((uint64_t *)key1.hash)[2] ^ ((uint64_t *)key2.hash)[2]; m.metric_ll[2] = key1.hash_ll[2] ^ key2.hash_ll[2];
((uint64_t *)m.metric)[3] = ((uint64_t *)key1.hash)[3] ^ ((uint64_t *)key2.hash)[3]; m.metric_ll[3] = key1.hash_ll[3] ^ key2.hash_ll[3];
return m; return m;
} }
} }

19
Identity.h

@ -34,8 +34,8 @@ namespace data
uint8_t certificate[3]; uint8_t certificate[3];
Identity& operator=(const Keys& keys); Identity& operator=(const Keys& keys);
bool FromBase64(const std::string&); bool FromBase64(const std::string& );
IdentHash Hash(); IdentHash Hash() const;
}; };
struct PrivateKeys // for eepsites struct PrivateKeys // for eepsites
@ -44,6 +44,10 @@ namespace data
uint8_t privateKey[256]; uint8_t privateKey[256];
uint8_t signingPrivateKey[20]; uint8_t signingPrivateKey[20];
PrivateKeys () = default;
PrivateKeys (const PrivateKeys& ) = default;
PrivateKeys (const Keys& keys) { *this = keys; };
PrivateKeys& operator=(const Keys& 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); };
bool operator< (const IdentHash& other) const { return memcmp (m_Hash, other.m_Hash, 32) < 0; }; 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: private:
@ -85,14 +89,19 @@ namespace data
void CreateRandomDHKeysPair (DHKeysPair * keys); // for transport sessions void CreateRandomDHKeysPair (DHKeysPair * keys); // for transport sessions
// kademlia // kademlia
struct RoutingKey union RoutingKey
{ {
uint8_t hash[32]; uint8_t hash[32];
uint64_t hash_ll[4];
}; };
struct XORMetric struct XORMetric
{ {
uint8_t metric[32]; union
{
uint8_t metric[32];
uint64_t metric_ll[4];
};
void SetMin () { memset (metric, 0, 32); }; void SetMin () { memset (metric, 0, 32); };
void SetMax () { memset (metric, 0xFF, 32); }; void SetMax () { memset (metric, 0xFF, 32); };

23
Log.cpp

@ -1,20 +1,29 @@
#include "Log.h" #include "Log.h"
#include "Daemon.h"
Log g_Log; Log g_Log;
void LogMsg::Process() void LogMsg::Process()
{ {
if (Daemon.isLogging == 1 && Daemon.logfile.is_open())
Daemon.logfile << s.str();
output << s.str(); output << s.str();
std::cout << s.str (); // TODO: delete later
} }
void Log::Flush () void Log::Flush ()
{ {
if (Daemon.isLogging == 1 && Daemon.logfile.is_open()) if (m_LogFile)
Daemon.logfile.flush(); 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;
}
}

14
Log.h

@ -1,8 +1,10 @@
#ifndef LOG_H__ #ifndef LOG_H__
#define LOG_H__ #define LOG_H__
#include <string>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <fstream>
#include <functional> #include <functional>
#include "Queue.h" #include "Queue.h"
@ -20,11 +22,19 @@ class Log: public i2p::util::MsgQueue<LogMsg>
{ {
public: 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: private:
void Flush (); void Flush ();
private:
std::ofstream * m_LogFile;
}; };
extern Log g_Log; extern Log g_Log;
@ -45,7 +55,7 @@ void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
template<typename... TArgs> template<typename... TArgs>
void LogPrint (TArgs... args) void LogPrint (TArgs... args)
{ {
LogMsg * msg = new LogMsg (); LogMsg * msg = g_Log.GetLogFile () ? new LogMsg (*g_Log.GetLogFile ()) : new LogMsg ();
LogPrint (msg->s, args...); LogPrint (msg->s, args...);
msg->s << std::endl; msg->s << std::endl;
g_Log.Put (msg); g_Log.Put (msg);

13
Makefile

@ -1,16 +1,21 @@
CC = g++ CC = g++
CFLAGS = -g -Wall -std=c++0x 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/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/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/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/UPnP.o obj/TunnelPool.o obj/HTTPProxy.o obj/AddressBook.o obj/Daemon.o \
obj/Daemon.o obj/DaemonLinux.o obj/SSUData.o obj/DaemonLinux.o obj/SSUData.o obj/i2p.o obj/aes.o
INCFLAGS = INCFLAGS =
LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
LIBS = LIBS =
#check if AES-NI is supported by CPU
ifneq ($(shell grep -c aes /proc/cpuinfo),0)
CPU_FLAGS = -DAESNI
endif
all: obj i2p all: obj i2p
i2p: $(OBJECTS:obj/%=obj/%) i2p: $(OBJECTS:obj/%=obj/%)
@ -20,7 +25,7 @@ i2p: $(OBJECTS:obj/%=obj/%)
.SUFFIXES: .c .cc .C .cpp .o .SUFFIXES: .c .cc .C .cpp .o
obj/%.o : %.cpp obj/%.o : %.cpp
$(CC) -o $@ $< -c $(CFLAGS) $(INCFLAGS) $(CC) -o $@ $< -c $(CFLAGS) $(INCFLAGS) $(CPU_FLAGS)
obj: obj:
mkdir -p obj mkdir -p obj

43
Makefile.osx vendored

@ -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

30
NTCPSession.cpp

@ -183,10 +183,12 @@ namespace ntcp
uint8_t aesKey[32]; uint8_t aesKey[32];
CreateAESKey (m_Phase1.pubKey, aesKey); CreateAESKey (m_Phase1.pubKey, aesKey);
m_Encryption.SetKeyWithIV (aesKey, 32, y + 240); m_Encryption.SetKey (aesKey);
m_Decryption.SetKeyWithIV (aesKey, 32, m_Phase1.HXxorHI + 16); 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::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)); 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]; uint8_t aesKey[32];
CreateAESKey (m_Phase2.pubKey, aesKey); CreateAESKey (m_Phase2.pubKey, aesKey);
m_Decryption.SetKeyWithIV (aesKey, 32, m_Phase2.pubKey + 240); m_Decryption.SetKey (aesKey);
m_Encryption.SetKeyWithIV (aesKey, 32, m_Phase1.HXxorHI + 16); 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 // verify
uint8_t xy[512], hxy[32]; uint8_t xy[512], hxy[32];
memcpy (xy, m_DHKeysPair->publicKey, 256); memcpy (xy, m_DHKeysPair->publicKey, 256);
@ -256,7 +260,7 @@ namespace ntcp
s.tsB = m_Phase2.encrypted.timestamp; s.tsB = m_Phase2.encrypted.timestamp;
i2p::context.Sign ((uint8_t *)&s, sizeof (s), m_Phase3.signature); 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::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)); boost::bind(&NTCPSession::HandlePhase3Sent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, tsA));
@ -288,7 +292,7 @@ namespace ntcp
else else
{ {
LogPrint ("Phase 3 received: ", bytes_transferred); 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); m_RemoteRouterInfo.SetRouterIdentity (m_Phase3.ident);
SignedData s; SignedData s;
@ -321,7 +325,7 @@ namespace ntcp
s.tsA = m_Phase3.timestamp; s.tsA = m_Phase3.timestamp;
s.tsB = tsB; s.tsB = tsB;
i2p::context.Sign ((uint8_t *)&s, sizeof (s), m_Phase4.signature); 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::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)); boost::bind(&NTCPSession::HandlePhase4Sent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
@ -355,7 +359,7 @@ namespace ntcp
else else
{ {
LogPrint ("Phase 4 received: ", bytes_transferred); 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 // verify signature
SignedData s; SignedData s;
@ -426,7 +430,7 @@ namespace ntcp
m_NextMessage = i2p::NewI2NPMessage (); m_NextMessage = i2p::NewI2NPMessage ();
m_NextMessageOffset = 0; 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); uint16_t dataSize = be16toh (*(uint16_t *)m_NextMessage->buf);
if (dataSize) if (dataSize)
{ {
@ -446,7 +450,7 @@ namespace ntcp
} }
else // message continues 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; m_NextMessageOffset += 16;
} }
@ -490,7 +494,7 @@ namespace ntcp
m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding); m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding);
int l = len + padding + 6; 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::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)); boost::bind(&NTCPSession::HandleSent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, msg));

5
NTCPSession.h

@ -7,6 +7,7 @@
#include <cryptopp/modes.h> #include <cryptopp/modes.h>
#include <cryptopp/aes.h> #include <cryptopp/aes.h>
#include <cryptopp/adler32.h> #include <cryptopp/adler32.h>
#include "aes.h"
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
@ -123,8 +124,8 @@ namespace ntcp
bool m_IsEstablished; bool m_IsEstablished;
i2p::data::DHKeysPair * m_DHKeysPair; // X - for client and Y - for server i2p::data::DHKeysPair * m_DHKeysPair; // X - for client and Y - for server
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption; i2p::crypto::CBCDecryption m_Decryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption; i2p::crypto::CBCEncryption m_Encryption;
CryptoPP::Adler32 m_Adler; CryptoPP::Adler32 m_Adler;
i2p::data::RouterInfo& m_RemoteRouterInfo; i2p::data::RouterInfo& m_RemoteRouterInfo;

74
NetDb.cpp

@ -122,7 +122,10 @@ namespace data
} }
} }
else // if no new DatabaseStore coming, explore it 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 (); uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts - lastSave >= 60) // save routers and validate subscriptions every minute if (ts - lastSave >= 60) // save routers and validate subscriptions every minute
@ -532,40 +535,54 @@ namespace data
i2p::DeleteI2NPMessage (msg); i2p::DeleteI2NPMessage (msg);
} }
void NetDb::Explore () void NetDb::Explore (int numDestinations)
{ {
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr; auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr;
if (outbound && inbound) bool throughTunnels = outbound && inbound;
{
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint8_t randomHash[32]; uint8_t randomHash[32];
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
std::set<const RouterInfo *> floodfills;
LogPrint ("Exploring new ", numDestinations, " routers ...");
for (int i = 0; i < numDestinations; i++)
{
rnd.GenerateBlock (randomHash, 32); rnd.GenerateBlock (randomHash, 32);
RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true); RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true);
dest->SetLastOutboundTunnel (outbound);
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ()); auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
if (floodfill) if (floodfill && !floodfills.count (floodfill)) // request floodfill only once
{ {
LogPrint ("Exploring new routers ..."); floodfills.insert (floodfill);
std::vector<i2p::tunnel::TunnelMessageBlock> msgs; if (throughTunnels)
msgs.push_back (i2p::tunnel::TunnelMessageBlock {
{ dest->SetLastOutboundTunnel (outbound);
i2p::tunnel::eDeliveryTypeRouter, msgs.push_back (i2p::tunnel::TunnelMessageBlock
floodfill->GetIdentHash (), 0, {
CreateDatabaseStoreMsg () // tell floodfill about us i2p::tunnel::eDeliveryTypeRouter,
}); floodfill->GetIdentHash (), 0,
msgs.push_back (i2p::tunnel::TunnelMessageBlock CreateDatabaseStoreMsg () // tell floodfill about us
{ });
i2p::tunnel::eDeliveryTypeRouter, msgs.push_back (i2p::tunnel::TunnelMessageBlock
floodfill->GetIdentHash (), 0, {
dest->CreateRequestMessage (floodfill, inbound) // explore i2p::tunnel::eDeliveryTypeRouter,
}); floodfill->GetIdentHash (), 0,
outbound->SendTunnelDataMsg (msgs); dest->CreateRequestMessage (floodfill, inbound) // explore
});
}
else
{
dest->SetLastOutboundTunnel (nullptr);
dest->SetLastReplyTunnel (nullptr);
i2p::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
}
} }
else else
DeleteRequestedDestination (dest); DeleteRequestedDestination (dest);
} }
if (throughTunnels && msgs.size () > 0)
outbound->SendTunnelDataMsg (msgs);
} }
void NetDb::Publish () 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 (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1); uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1);
@ -627,9 +644,8 @@ namespace data
{ {
if (i >= ind) if (i >= ind)
{ {
if (!it.second->IsUnreachable () && if (!it.second->IsUnreachable () && !it.second->IsHidden () &&
(!compatibleWith || it.second->IsCompatible (*compatibleWith)) && (!compatibleWith || it.second->IsCompatible (*compatibleWith)))
(!caps || (it.second->GetCaps () & caps) == caps))
return it.second; return it.second;
} }
else else

10
NetDb.h

@ -33,6 +33,7 @@ namespace data
void ClearExcludedPeers (); void ClearExcludedPeers ();
const RouterInfo * GetLastRouter () const { return m_LastRouter; }; const RouterInfo * GetLastRouter () const { return m_LastRouter; };
const i2p::tunnel::InboundTunnel * GetLastReplyTunnel () const { return m_LastReplyTunnel; }; 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 IsExploratory () const { return m_IsExploratory; };
bool IsLeaseSet () const { return m_IsLeaseSet; }; bool IsLeaseSet () const { return m_IsLeaseSet; };
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); }; 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 HandleDatabaseStoreMsg (uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg); 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); 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: private:
@ -85,7 +91,7 @@ namespace data
void Load (const char * directory); void Load (const char * directory);
void SaveUpdated (const char * directory); void SaveUpdated (const char * directory);
void Run (); // exploratory thread void Run (); // exploratory thread
void Explore (); void Explore (int numDestinations);
void Publish (); void Publish ();
void ValidateSubscriptions (); void ValidateSubscriptions ();
const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const; const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;

5
RouterInfo.cpp

@ -214,7 +214,10 @@ namespace data
break; break;
case 'C': case 'C':
m_Caps |= Caps::eSSUIntroducer; m_Caps |= Caps::eSSUIntroducer;
break; break;
case 'H':
m_Caps |= Caps::eHidden;
break;
default: ; default: ;
} }
cap++; cap++;

5
RouterInfo.h

@ -31,7 +31,8 @@ namespace data
eHighBandwidth = 0x02, eHighBandwidth = 0x02,
eReachable = 0x04, eReachable = 0x04,
eSSUTesting = 0x08, eSSUTesting = 0x08,
eSSUIntroducer = 0x10 eSSUIntroducer = 0x10,
eHidden = 0x20
}; };
enum TransportStyle enum TransportStyle
@ -88,6 +89,8 @@ namespace data
bool UsesIntroducer () const; bool UsesIntroducer () const;
bool IsIntroducer () const { return m_Caps & eSSUIntroducer; }; bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
bool IsPeerTesting () const { return m_Caps & eSSUTesting; }; bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
bool IsHidden () const { return m_Caps & eHidden; };
uint8_t GetCaps () const { return m_Caps; }; uint8_t GetCaps () const { return m_Caps; };
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };

209
SSU.cpp

@ -29,7 +29,7 @@ namespace ssu
delete m_DHKeysPair; 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); CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
uint8_t sharedKey[256]; uint8_t sharedKey[256];
@ -41,14 +41,14 @@ namespace ssu
if (sharedKey[0] & 0x80) if (sharedKey[0] & 0x80)
{ {
aesKey[0] = 0; m_SessionKey[0] = 0;
memcpy (aesKey + 1, sharedKey, 31); memcpy (m_SessionKey + 1, sharedKey, 31);
memcpy (macKey, sharedKey + 31, 32); memcpy (m_MacKey, sharedKey + 31, 32);
} }
else if (sharedKey[0]) else if (sharedKey[0])
{ {
memcpy (aesKey, sharedKey, 32); memcpy (m_SessionKey, sharedKey, 32);
memcpy (macKey, sharedKey + 32, 32); memcpy (m_MacKey, sharedKey + 32, 32);
} }
else else
{ {
@ -64,10 +64,12 @@ namespace ssu
} }
} }
memcpy (aesKey, nonZero, 32); memcpy (m_SessionKey, nonZero, 32);
CryptoPP::SHA256().CalculateDigest(macKey, nonZero, 64 - (nonZero - sharedKey)); CryptoPP::SHA256().CalculateDigest(m_MacKey, nonZero, 64 - (nonZero - sharedKey));
} }
m_IsSessionKey = true; 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) void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
@ -82,8 +84,13 @@ namespace ssu
else else
{ {
ScheduleTermination (); 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 if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first
Decrypt (buf, len, m_SessionKey); DecryptSessionKey (buf, len);
else else
{ {
// try intro key depending on side // 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) void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
m_State = eSessionStateRequestReceived;
LogPrint ("Session request received"); LogPrint ("Session request received");
m_RemoteEndpoint = senderEndpoint; m_RemoteEndpoint = senderEndpoint;
CreateAESandMacKey (buf + sizeof (SSUHeader), m_SessionKey, m_MacKey); CreateAESandMacKey (buf + sizeof (SSUHeader));
SendSessionCreated (buf + sizeof (SSUHeader)); SendSessionCreated (buf + sizeof (SSUHeader));
} }
@ -176,13 +182,12 @@ namespace ssu
return; return;
} }
m_State = eSessionStateCreatedReceived;
LogPrint ("Session created received"); LogPrint ("Session created received");
m_Timer.cancel (); // connect timer 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 signedData[532]; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
uint8_t * y = payload; uint8_t * y = payload;
CreateAESandMacKey (y, m_SessionKey, m_MacKey); CreateAESandMacKey (y);
memcpy (signedData, m_DHKeysPair->publicKey, 256); // x memcpy (signedData, m_DHKeysPair->publicKey, 256); // x
memcpy (signedData + 256, y, 256); // y memcpy (signedData + 256, y, 256); // y
payload += 256; payload += 256;
@ -202,8 +207,8 @@ namespace ssu
payload += 4; // relayTag payload += 4; // relayTag
payload += 4; // signed on time payload += 4; // signed on time
// decrypt DSA signature // decrypt DSA signature
m_Decryption.SetKeyWithIV (m_SessionKey, 32, ((SSUHeader *)buf)->iv); m_SessionKeyDecryption.SetIV (((SSUHeader *)buf)->iv);
m_Decryption.ProcessData (payload, payload, 48); m_SessionKeyDecryption.Decrypt (payload, 48, payload);
// verify // verify
CryptoPP::DSA::PublicKey pubKey; CryptoPP::DSA::PublicKey pubKey;
pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, CryptoPP::Integer (m_RemoteRouter->GetRouterIdentity ().signingKey, 128)); 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) void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len)
{ {
m_State = eSessionStateConfirmedReceived;
LogPrint ("Session confirmed received"); LogPrint ("Session confirmed received");
m_State = eSessionStateEstablished;
SendI2NPMessage (CreateDeliveryStatusMsg (0)); SendI2NPMessage (CreateDeliveryStatusMsg (0));
Established (); Established ();
} }
@ -242,8 +245,6 @@ namespace ssu
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv rnd.GenerateBlock (iv, 16); // random iv
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, 304, introKey, iv, introKey); FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, 304, introKey, iv, introKey);
m_State = eSessionStateRequestSent;
m_Server.Send (buf, 304, m_RemoteEndpoint); m_Server.Send (buf, 304, m_RemoteEndpoint);
} }
@ -276,10 +277,7 @@ namespace ssu
if (m_State == eSessionStateEstablished) if (m_State == eSessionStateEstablished)
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, m_SessionKey, iv, m_MacKey); FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, m_SessionKey, iv, m_MacKey);
else else
{ FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, iKey, iv, iKey);
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, iKey, iv, iKey);
m_State = eSessionStateRelayRequestSent;
}
m_Server.Send (buf, 96, m_RemoteEndpoint); m_Server.Send (buf, 96, m_RemoteEndpoint);
} }
@ -327,13 +325,12 @@ namespace ssu
uint8_t iv[16]; uint8_t iv[16];
rnd.GenerateBlock (iv, 16); // random iv rnd.GenerateBlock (iv, 16); // random iv
// encrypt signature and 8 bytes padding with newly created session key // encrypt signature and 8 bytes padding with newly created session key
m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); m_SessionKeyEncryption.SetIV (iv);
m_Encryption.ProcessData (payload, payload, 48); m_SessionKeyEncryption.Encrypt (payload, 48, payload);
// encrypt message with intro key // encrypt message with intro key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, introKey, iv, introKey); FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, introKey, iv, introKey);
m_State = eSessionStateCreatedSent; Send (buf, 368);
m_Server.Send (buf, 368, m_RemoteEndpoint);
} }
void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress) void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress)
@ -371,8 +368,7 @@ namespace ssu
rnd.GenerateBlock (iv, 16); // random iv rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with session key // encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CONFIRMED, buf, 480, m_SessionKey, iv, m_MacKey); FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CONFIRMED, buf, 480, m_SessionKey, iv, m_MacKey);
m_State = eSessionStateConfirmedSent; Send (buf, 480);
m_Server.Send (buf, 480, m_RemoteEndpoint);
} }
void SSUSession::ProcessRelayRequest (uint8_t * buf, size_t len) void SSUSession::ProcessRelayRequest (uint8_t * buf, size_t len)
@ -499,15 +495,37 @@ namespace ssu
header->time = htobe32 (i2p::util::GetSecondsSinceEpoch ()); header->time = htobe32 (i2p::util::GetSecondsSinceEpoch ());
uint8_t * encrypted = &header->flag; uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf); uint16_t encryptedLen = len - (encrypted - buf);
m_Encryption.SetKeyWithIV (aesKey, 32, iv); i2p::crypto::CBCEncryption encryption;
encryptedLen = (encryptedLen>>4)<<4; // make sure 16 bytes boundary encryption.SetKey (aesKey);
m_Encryption.ProcessData (encrypted, encrypted, encryptedLen); encryption.SetIV (iv);
encryption.Encrypt (encrypted, encryptedLen, encrypted);
// assume actual buffer size is 18 (16 + 2) bytes more // assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, iv, 16); memcpy (buf + len, iv, 16);
*(uint16_t *)(buf + len + 16) = htobe16 (encryptedLen); *(uint16_t *)(buf + len + 16) = htobe16 (encryptedLen);
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac); 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) void SSUSession::Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey)
{ {
if (len < sizeof (SSUHeader)) if (len < sizeof (SSUHeader))
@ -518,11 +536,29 @@ namespace ssu
SSUHeader * header = (SSUHeader *)buf; SSUHeader * header = (SSUHeader *)buf;
uint8_t * encrypted = &header->flag; uint8_t * encrypted = &header->flag;
uint16_t encryptedLen = len - (encrypted - buf); uint16_t encryptedLen = len - (encrypted - buf);
m_Decryption.SetKeyWithIV (aesKey, 32, header->iv); i2p::crypto::CBCDecryption decryption;
encryptedLen = (encryptedLen>>4)<<4; // make sure 16 bytes boundary decryption.SetKey (aesKey);
m_Decryption.ProcessData (encrypted, encrypted, encryptedLen); 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) bool SSUSession::Validate (uint8_t * buf, size_t len, const uint8_t * macKey)
{ {
if (len < sizeof (SSUHeader)) if (len < sizeof (SSUHeader))
@ -602,7 +638,7 @@ namespace ssu
if (!m_DelayedMessages.empty ()) if (!m_DelayedMessages.empty ())
{ {
for (auto it :m_DelayedMessages) for (auto it :m_DelayedMessages)
Send (it); m_Data.Send (it);
m_DelayedMessages.clear (); m_DelayedMessages.clear ();
} }
if (m_PeerTest && (m_RemoteRouter && m_RemoteRouter->IsPeerTesting ())) if (m_PeerTest && (m_RemoteRouter && m_RemoteRouter->IsPeerTesting ()))
@ -658,7 +694,7 @@ namespace ssu
if (msg) if (msg)
{ {
if (m_State == eSessionStateEstablished) if (m_State == eSessionStateEstablished)
Send (msg); m_Data.Send (msg);
else else
m_DelayedMessages.push_back (msg); m_DelayedMessages.push_back (msg);
} }
@ -771,100 +807,26 @@ namespace ssu
memset (payload, 0, 6); // address and port always zero for Alice memset (payload, 0, 6); // address and port always zero for Alice
payload += 6; // address and port payload += 6; // address and port
memcpy (payload, introKey, 32); // intro key memcpy (payload, introKey, 32); // intro key
uint8_t iv[16];
rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with session key // encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, m_SessionKey, iv, m_MacKey); FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80);
m_Server.Send (buf, 80, m_RemoteEndpoint); 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 () void SSUSession::SendSesionDestroyed ()
{ {
if (m_IsSessionKey) if (m_IsSessionKey)
{ {
uint8_t buf[48 + 18], iv[16]; uint8_t buf[48 + 18];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with session key // encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey); FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48);
m_Server.Send (buf, 48, m_RemoteEndpoint); 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) void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len)
{ {
uint8_t buf[SSU_MTU + 18]; uint8_t buf[SSU_MTU + 18];
uint8_t iv[16];
size_t msgSize = len + sizeof (SSUHeader); size_t msgSize = len + sizeof (SSUHeader);
if (msgSize > SSU_MTU) if (msgSize > SSU_MTU)
{ {
@ -872,13 +834,16 @@ namespace ssu
return; return;
} }
memcpy (buf + sizeof (SSUHeader), payload, len); memcpy (buf + sizeof (SSUHeader), payload, len);
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with session key // encrypt message with session key
FillHeaderAndEncrypt (type, buf, msgSize, m_SessionKey, iv, m_MacKey); FillHeaderAndEncrypt (type, buf, msgSize);
m_Server.Send (buf, msgSize, m_RemoteEndpoint); 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), 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) m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (m_Service, m_Endpoint)
{ {
@ -941,7 +906,7 @@ namespace ssu
return nullptr; 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); m_Socket.send_to (boost::asio::buffer (buf, len), to);
LogPrint ("SSU sent ", len, " bytes"); LogPrint ("SSU sent ", len, " bytes");

46
SSU.h

@ -2,13 +2,13 @@
#define SSU_H__ #define SSU_H__
#include <inttypes.h> #include <inttypes.h>
#include <string.h>
#include <map> #include <map>
#include <list> #include <list>
#include <set> #include <set>
#include <thread> #include <thread>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <cryptopp/modes.h> #include "aes.h"
#include <cryptopp/aes.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
@ -48,15 +48,7 @@ namespace ssu
enum SessionState enum SessionState
{ {
eSessionStateUnknown, eSessionStateUnknown,
eSessionStateRequestSent,
eSessionStateRequestReceived,
eSessionStateCreatedSent,
eSessionStateCreatedReceived,
eSessionStateConfirmedSent,
eSessionStateConfirmedReceived,
eSessionStateRelayRequestSent,
eSessionStateRelayRequestReceived,
eSessionStateIntroduced, eSessionStateIntroduced,
eSessionStateEstablished, eSessionStateEstablished,
eSessionStateFailed eSessionStateFailed
@ -85,7 +77,7 @@ namespace ssu
private: 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 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); 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 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 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 ProcessData (uint8_t * buf, size_t len);
void SendMsgAck (uint32_t msgID);
void SendSesionDestroyed (); void SendSesionDestroyed ();
void Send (i2p::I2NPMessage * msg);
void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key 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 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); bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey);
const uint8_t * GetIntroKey () const; const uint8_t * GetIntroKey () const;
@ -120,7 +113,21 @@ namespace ssu
void HandleTerminationTimer (const boost::system::error_code& ecode); void HandleTerminationTimer (const boost::system::error_code& ecode);
private: 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 friend class SSUData; // TODO: change in later
SSUServer& m_Server; SSUServer& m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint; boost::asio::ip::udp::endpoint m_RemoteEndpoint;
@ -132,10 +139,11 @@ namespace ssu
bool m_IsSessionKey; bool m_IsSessionKey;
uint32_t m_RelayTag; uint32_t m_RelayTag;
std::set<uint32_t> m_PeerTestNonces; std::set<uint32_t> m_PeerTestNonces;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption; i2p::crypto::CBCEncryption m_SessionKeyEncryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption; i2p::crypto::CBCDecryption m_SessionKeyDecryption;
uint8_t m_SessionKey[32], m_MacKey[32]; uint8_t m_SessionKey[32], m_MacKey[32];
std::list<i2p::I2NPMessage *> m_DelayedMessages; std::list<i2p::I2NPMessage *> m_DelayedMessages;
std::set<IV> m_ReceivedIVs;
SSUData m_Data; SSUData m_Data;
}; };
@ -155,7 +163,7 @@ namespace ssu
boost::asio::io_service& GetService () { return m_Socket.get_io_service(); }; boost::asio::io_service& GetService () { return m_Socket.get_io_service(); };
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; }; 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); void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay);
SSUSession * FindRelaySession (uint32_t tag); SSUSession * FindRelaySession (uint32_t tag);

95
SSUData.cpp

@ -19,8 +19,25 @@ namespace ssu
DeleteI2NPMessage (it.second->msg); DeleteI2NPMessage (it.second->msg);
delete it.second; 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) void SSUData::ProcessMessage (uint8_t * buf, size_t len)
{ {
//uint8_t * start = buf; //uint8_t * start = buf;
@ -32,7 +49,8 @@ namespace ssu
// explicit ACKs // explicit ACKs
uint8_t numAcks =*buf; uint8_t numAcks =*buf;
buf++; buf++;
// TODO: process ACKs for (int i = 0; i < numAcks; i++)
ProcessSentMessageAck (be32toh (((uint32_t *)buf)[i]));
buf += numAcks*4; buf += numAcks*4;
} }
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
@ -43,7 +61,7 @@ namespace ssu
for (int i = 0; i < numBitfields; i++) for (int i = 0; i < numBitfields; i++)
{ {
buf += 4; // msgID buf += 4; // msgID
// TODO: process ACH bitfields // TODO: process individual Ack bitfields
while (*buf & 0x80) // not last while (*buf & 0x80) // not last
buf++; buf++;
buf++; // last byte buf++; // last byte
@ -90,6 +108,8 @@ namespace ssu
if (isLast) if (isLast)
{ {
if (!msg)
DeleteI2NPMessage (it->second->msg);
delete it->second; delete it->second;
m_IncomleteMessages.erase (it); m_IncomleteMessages.erase (it);
} }
@ -111,7 +131,7 @@ namespace ssu
m_IncomleteMessages[msgID] = new IncompleteMessage (msg); m_IncomleteMessages[msgID] = new IncompleteMessage (msg);
if (isLast) if (isLast)
{ {
m_Session.SendMsgAck (msgID); SendMsgAck (msgID);
msg->FromSSU (msgID); msg->FromSSU (msgID);
if (m_Session.GetState () == eSessionStateEstablished) if (m_Session.GetState () == eSessionStateEstablished)
i2p::HandleI2NPMessage (msg); 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);
}
} }
} }

8
SSUData.h

@ -3,6 +3,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <map> #include <map>
#include <vector>
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
namespace i2p namespace i2p
@ -27,6 +28,12 @@ namespace ssu
~SSUData (); ~SSUData ();
void ProcessMessage (uint8_t * buf, size_t len); 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: private:
@ -40,6 +47,7 @@ namespace ssu
SSUSession& m_Session; SSUSession& m_Session;
std::map<uint32_t, IncompleteMessage *> m_IncomleteMessages; std::map<uint32_t, IncompleteMessage *> m_IncomleteMessages;
std::map<uint32_t, std::vector<uint8_t *> > m_SentMessages; // msgID -> fragments
}; };
} }
} }

9
Streaming.cpp

@ -334,12 +334,13 @@ namespace stream
StreamingDestination::StreamingDestination (): m_LeaseSet (nullptr) StreamingDestination::StreamingDestination (): m_LeaseSet (nullptr)
{ {
m_Keys = i2p::data::CreateRandomKeys (); m_Keys = i2p::data::CreateRandomKeys ();
m_IdentHash = m_Keys.pub.Hash (); m_IdentHash = m_Keys.pub.Hash ();
m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
CryptoPP::Integer (m_Keys.signingPrivateKey, 20)); CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey); 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) StreamingDestination::StreamingDestination (const std::string& fullPath): m_LeaseSet (nullptr)
@ -349,9 +350,13 @@ namespace stream
s.read ((char *)&m_Keys, sizeof (m_Keys)); s.read ((char *)&m_Keys, sizeof (m_Keys));
else else
LogPrint ("Can't open file ", fullPath); 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); CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey); 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 () StreamingDestination::~StreamingDestination ()

16
TransitTunnel.cpp

@ -17,22 +17,12 @@ namespace tunnel
m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID), m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID),
m_NextIdent (nextIdent), m_NumTransmittedBytes (0) m_NextIdent (nextIdent), m_NumTransmittedBytes (0)
{ {
memcpy (m_LayerKey, layerKey, 32); m_Encryption.SetKeys (layerKey, ivKey);
memcpy (m_IVKey, ivKey, 32);
} }
void TransitTunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg) void TransitTunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg)
{ {
uint8_t * payload = tunnelMsg->GetPayload () + 4; m_Encryption.Encrypt (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
} }
void TransitTunnel::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg) void TransitTunnel::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)

8
TransitTunnel.h

@ -2,8 +2,7 @@
#define TRANSIT_TUNNEL_H__ #define TRANSIT_TUNNEL_H__
#include <inttypes.h> #include <inttypes.h>
#include <cryptopp/modes.h> #include "aes.h"
#include <cryptopp/aes.h>
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "TunnelEndpoint.h" #include "TunnelEndpoint.h"
#include "TunnelGateway.h" #include "TunnelGateway.h"
@ -36,12 +35,9 @@ namespace tunnel
uint32_t m_TunnelID, m_NextTunnelID; uint32_t m_TunnelID, m_NextTunnelID;
i2p::data::IdentHash m_NextIdent; i2p::data::IdentHash m_NextIdent;
uint8_t m_LayerKey[32];
uint8_t m_IVKey[32];
size_t m_NumTransmittedBytes; size_t m_NumTransmittedBytes;
CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_ECBEncryption; i2p::crypto::TunnelEncryption m_Encryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_CBCEncryption;
}; };
class TransitTunnelGateway: public TransitTunnel class TransitTunnelGateway: public TransitTunnel

91
Tunnel.cpp

@ -27,7 +27,7 @@ namespace tunnel
void Tunnel::Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel) void Tunnel::Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel)
{ {
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); 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 (); I2NPMessage * msg = NewI2NPMessage ();
*msg->GetPayload () = numRecords; *msg->GetPayload () = numRecords;
msg->len += numRecords*sizeof (I2NPBuildRequestRecordElGamalEncrypted) + 1; 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->next ? rnd.GenerateWord32 () : replyMsgID, // we set replyMsgID for last hop only
hop->isGateway, hop->isEndpoint), hop->isGateway, hop->isEndpoint),
records[i]); records[i]);
hop->recordIndex = i; //TODO:
i++; i++;
hop = hop->next; 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; hop = m_Config->GetLastHop ()->prev;
size_t ind = numRecords - 1;
while (hop) 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); decryption.SetIV (hop->replyIV);
m_CBCDecryption.ProcessData((uint8_t *)&records[i], (uint8_t *)&records[i], sizeof (I2NPBuildRequestRecordElGamalEncrypted)); decryption.Decrypt((uint8_t *)&records[hop1->recordIndex],
sizeof (I2NPBuildRequestRecordElGamalEncrypted),
(uint8_t *)&records[hop1->recordIndex]);
hop1 = hop1->next;
} }
hop = hop->prev; hop = hop->prev;
ind--;
} }
FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild); FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild);
@ -75,45 +87,53 @@ namespace tunnel
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
{ {
LogPrint ("TunnelBuildResponse ", (int)msg[0], " records."); LogPrint ("TunnelBuildResponse ", (int)msg[0], " records.");
i2p::crypto::CBCDecryption decryption;
TunnelHopConfig * hop = m_Config->GetLastHop (); TunnelHopConfig * hop = m_Config->GetLastHop ();
int num = msg[0];
while (hop) while (hop)
{ {
for (int i = 0; i < num; i++) decryption.SetKey (hop->replyKey);
{ // decrypt records before and including current hop
uint8_t * record = msg + 1 + i*sizeof (I2NPBuildResponseRecord); TunnelHopConfig * hop1 = hop;
m_CBCDecryption.SetKeyWithIV(hop->replyKey, 32, hop->replyIV); while (hop1)
m_CBCDecryption.ProcessData(record, record, sizeof (I2NPBuildResponseRecord)); {
} 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; hop = hop->prev;
num--;
} }
m_IsEstablished = true; 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); LogPrint ("Ret code=", (int)record->ret);
if (record->ret) if (record->ret)
// if any of participants declined the tunnel is not established // if any of participants declined the tunnel is not established
m_IsEstablished = false; 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; 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) void Tunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg)
{ {
@ -121,10 +141,7 @@ namespace tunnel
TunnelHopConfig * hop = m_Config->GetLastHop (); TunnelHopConfig * hop = m_Config->GetLastHop ();
while (hop) while (hop)
{ {
// iv + data hop->decryption.Decrypt (payload);
IVDecrypt (payload, hop->ivKey, payload);
LayerDecrypt (payload + 16, TUNNEL_DATA_ENCRYPTED_SIZE, hop->layerKey, payload, payload+16);
IVDecrypt (payload, hop->ivKey, payload);
hop = hop->prev; hop = hop->prev;
} }
} }
@ -258,9 +275,9 @@ namespace tunnel
return 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; m_Pools[pool->GetIdentHash ()] = pool;
return pool; return pool;
} }
@ -424,7 +441,7 @@ namespace tunnel
LogPrint ("Creating zero hops inbound tunnel..."); LogPrint ("Creating zero hops inbound tunnel...");
CreateZeroHopsInboundTunnel (); CreateZeroHopsInboundTunnel ();
if (!m_ExploratoryPool) if (!m_ExploratoryPool)
m_ExploratoryPool = CreateTunnelPool (i2p::context); m_ExploratoryPool = CreateTunnelPool (i2p::context, 2); // 2-hop exploratory
return; return;
} }

15
Tunnel.h

@ -8,8 +8,6 @@
#include <string> #include <string>
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
#include "Queue.h" #include "Queue.h"
#include "TunnelConfig.h" #include "TunnelConfig.h"
#include "TunnelPool.h" #include "TunnelPool.h"
@ -51,20 +49,11 @@ namespace tunnel
uint32_t GetNextTunnelID () const { return m_Config->GetFirstHop ()->tunnelID; }; uint32_t GetNextTunnelID () const { return m_Config->GetFirstHop ()->tunnelID; };
const i2p::data::IdentHash& GetNextIdentHash () const { return m_Config->GetFirstHop ()->router->GetIdentHash (); }; 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: private:
TunnelConfig * m_Config; TunnelConfig * m_Config;
TunnelPool * m_Pool; // pool, tunnel belongs to, or null TunnelPool * m_Pool; // pool, tunnel belongs to, or null
bool m_IsEstablished, m_IsFailed; bool m_IsEstablished, m_IsFailed;
CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption m_ECBDecryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_CBCDecryption;
}; };
class OutboundTunnel: public Tunnel class OutboundTunnel: public Tunnel
@ -125,7 +114,7 @@ namespace tunnel
void PostTunnelData (I2NPMessage * msg); void PostTunnelData (I2NPMessage * msg);
template<class TTunnel> template<class TTunnel>
TTunnel * CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel = 0); 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); void DeleteTunnelPool (TunnelPool * pool);
private: private:

11
TunnelConfig.h

@ -4,6 +4,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include "aes.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "RouterContext.h" #include "RouterContext.h"
@ -22,7 +23,9 @@ namespace tunnel
bool isGateway, isEndpoint; bool isGateway, isEndpoint;
TunnelHopConfig * next, * prev; TunnelHopConfig * next, * prev;
i2p::crypto::TunnelDecryption decryption;
int recordIndex; // record # in tunnel build message
TunnelHopConfig (const i2p::data::RouterInfo * r) TunnelHopConfig (const i2p::data::RouterInfo * r)
{ {
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
@ -131,9 +134,9 @@ namespace tunnel
return m_LastHop; return m_LastHop;
} }
size_t GetNumHops () const int GetNumHops () const
{ {
size_t num = 0; int num = 0;
TunnelHopConfig * hop = m_FirstHop; TunnelHopConfig * hop = m_FirstHop;
while (hop) while (hop)
{ {
@ -180,6 +183,8 @@ namespace tunnel
newConfig->m_LastHop = newHop; newConfig->m_LastHop = newHop;
if (hop->isGateway) // inbound tunnel if (hop->isGateway) // inbound tunnel
newHop->SetReplyHop (m_FirstHop); // use it as reply 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 if (!hop->next) newConfig->m_FirstHop = newHop; // last hop

35
TunnelEndpoint.cpp

@ -26,7 +26,8 @@ namespace tunnel
bool isFollowOnFragment = flag & 0x80, isLastFragment = true; bool isFollowOnFragment = flag & 0x80, isLastFragment = true;
uint32_t msgID = 0; uint32_t msgID = 0;
TunnelMessageBlock m; int fragmentNum = 0;
TunnelMessageBlockEx m;
if (!isFollowOnFragment) if (!isFollowOnFragment)
{ {
// first fragment // first fragment
@ -68,7 +69,7 @@ namespace tunnel
// follow on // follow on
msgID = be32toh (*(uint32_t *)fragment); // MessageID msgID = be32toh (*(uint32_t *)fragment); // MessageID
fragment += 4; fragment += 4;
int fragmentNum = (flag >> 1) & 0x3F; // 6 bits fragmentNum = (flag >> 1) & 0x3F; // 6 bits
isLastFragment = flag & 0x01; isLastFragment = flag & 0x01;
LogPrint ("Follow on fragment ", fragmentNum, " of message ", msgID, isLastFragment ? " last" : " non-last"); 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 (msgID) // msgID is presented, assume message is fragmented
{ {
if (!isFollowOnFragment) // create new incomlete message if (!isFollowOnFragment) // create new incomlete message
{
m.nextFragmentNum = 1;
m_IncompleteMessages[msgID] = m; m_IncompleteMessages[msgID] = m;
}
else else
{ {
auto it = m_IncompleteMessages.find (msgID); auto it = m_IncompleteMessages.find (msgID);
if (it != m_IncompleteMessages.end()) if (it != m_IncompleteMessages.end())
{ {
I2NPMessage * incompleteMessage = it->second.data; if (fragmentNum == it->second.nextFragmentNum)
memcpy (incompleteMessage->buf + incompleteMessage->len, fragment, size); // concatenate fragment
incompleteMessage->len += size;
// TODO: check fragmentNum sequence
if (isLastFragment)
{ {
// message complete I2NPMessage * incompleteMessage = it->second.data;
HandleNextMessage (it->second); memcpy (incompleteMessage->buf + incompleteMessage->len, fragment, size); // concatenate fragment
m_IncompleteMessages.erase (it); 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 else
LogPrint ("First fragment of message ", msgID, " not found. Discarded"); LogPrint ("First fragment of message ", msgID, " not found. Discarded");

7
TunnelEndpoint.h

@ -26,7 +26,12 @@ namespace tunnel
private: private:
std::map<uint32_t, TunnelMessageBlock> m_IncompleteMessages; struct TunnelMessageBlockEx: public TunnelMessageBlock
{
uint8_t nextFragmentNum;
};
std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
size_t m_NumReceivedBytes; size_t m_NumReceivedBytes;
}; };
} }

23
TunnelPool.cpp

@ -10,8 +10,8 @@ namespace i2p
{ {
namespace tunnel namespace tunnel
{ {
TunnelPool::TunnelPool (i2p::data::LocalDestination& localDestination, int numTunnels): TunnelPool::TunnelPool (i2p::data::LocalDestination& localDestination, int numHops, int numTunnels):
m_LocalDestination (localDestination), m_NumTunnels (numTunnels), m_LastOutboundTunnel (nullptr) m_LocalDestination (localDestination), m_NumHops (numHops), m_NumTunnels (numTunnels), m_LastOutboundTunnel (nullptr)
{ {
} }
@ -189,15 +189,18 @@ namespace tunnel
if (inboundTunnel) if (inboundTunnel)
{ {
LogPrint ("Creating destination outbound tunnel..."); 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<const i2p::data::RouterInfo *> 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<OutboundTunnel> ( auto * tunnel = tunnels.CreateTunnel<OutboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *> new TunnelConfig (hops, inboundTunnel->GetTunnelConfig ()));
{
firstHop,
secondHop
},
inboundTunnel->GetTunnelConfig ()));
tunnel->SetTunnelPool (this); tunnel->SetTunnelPool (this);
} }
} }

4
TunnelPool.h

@ -23,7 +23,7 @@ namespace tunnel
{ {
public: public:
TunnelPool (i2p::data::LocalDestination& localDestination, int numTunnels = 5); TunnelPool (i2p::data::LocalDestination& localDestination, int numHops, int numTunnels = 5);
~TunnelPool (); ~TunnelPool ();
const uint8_t * GetEncryptionPrivateKey () const { return m_LocalDestination.GetEncryptionPrivateKey (); }; const uint8_t * GetEncryptionPrivateKey () const { return m_LocalDestination.GetEncryptionPrivateKey (); };
@ -53,7 +53,7 @@ namespace tunnel
private: private:
i2p::data::LocalDestination& m_LocalDestination; i2p::data::LocalDestination& m_LocalDestination;
int m_NumTunnels; int m_NumHops, m_NumTunnels;
std::set<InboundTunnel *, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first std::set<InboundTunnel *, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first
std::set<OutboundTunnel *, TunnelCreationTimeCmp> m_OutboundTunnels; std::set<OutboundTunnel *, TunnelCreationTimeCmp> m_OutboundTunnels;
std::map<uint32_t, std::pair<OutboundTunnel *, InboundTunnel *> > m_Tests; std::map<uint32_t, std::pair<OutboundTunnel *, InboundTunnel *> > m_Tests;

357
aes.cpp

@ -0,0 +1,357 @@
#include <stdlib.h>
#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
}
}
}

188
aes.h

@ -0,0 +1,188 @@
#ifndef AES_H__
#define AES_H__
#include <inttypes.h>
#include <cryptopp/modes.h>
#include <cryptopp/aes.h>
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<CryptoPP::AES>::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<CryptoPP::AES>::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

44
hmac.h

@ -18,38 +18,38 @@ namespace crypto
// digest is 16 bytes // digest is 16 bytes
// block size is 64 bytes // block size is 64 bytes
{ {
uint8_t buf[2048]; uint64_t buf[256];
// ikeypad // ikeypad
((uint64_t *)buf)[0] = ((uint64_t *)key)[0] ^ IPAD; buf[0] = ((uint64_t *)key)[0] ^ IPAD;
((uint64_t *)buf)[1] = ((uint64_t *)key)[1] ^ IPAD; buf[1] = ((uint64_t *)key)[1] ^ IPAD;
((uint64_t *)buf)[2] = ((uint64_t *)key)[2] ^ IPAD; buf[2] = ((uint64_t *)key)[2] ^ IPAD;
((uint64_t *)buf)[3] = ((uint64_t *)key)[3] ^ IPAD; buf[3] = ((uint64_t *)key)[3] ^ IPAD;
((uint64_t *)buf)[4] = IPAD; buf[4] = IPAD;
((uint64_t *)buf)[5] = IPAD; buf[5] = IPAD;
((uint64_t *)buf)[6] = IPAD; buf[6] = IPAD;
((uint64_t *)buf)[7] = IPAD; buf[7] = IPAD;
// concatenate with msg // concatenate with msg
memcpy (buf + 64, msg, len); memcpy (buf + 8, msg, len);
// calculate first hash // calculate first hash
uint8_t hash[16]; // MD5 uint8_t hash[16]; // MD5
CryptoPP::Weak1::MD5().CalculateDigest (hash, buf, len + 64); CryptoPP::Weak1::MD5().CalculateDigest (hash, (uint8_t *)buf, len + 64);
// okeypad // okeypad
((uint64_t *)buf)[0] = ((uint64_t *)key)[0] ^ OPAD; buf[0] = ((uint64_t *)key)[0] ^ OPAD;
((uint64_t *)buf)[1] = ((uint64_t *)key)[1] ^ OPAD; buf[1] = ((uint64_t *)key)[1] ^ OPAD;
((uint64_t *)buf)[2] = ((uint64_t *)key)[2] ^ OPAD; buf[2] = ((uint64_t *)key)[2] ^ OPAD;
((uint64_t *)buf)[3] = ((uint64_t *)key)[3] ^ OPAD; buf[3] = ((uint64_t *)key)[3] ^ OPAD;
((uint64_t *)buf)[4] = OPAD; buf[4] = OPAD;
((uint64_t *)buf)[5] = OPAD; buf[5] = OPAD;
((uint64_t *)buf)[6] = OPAD; buf[6] = OPAD;
((uint64_t *)buf)[7] = OPAD; buf[7] = OPAD;
// copy first hash after okeypad // 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) // 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 // calculate digest
CryptoPP::Weak1::MD5().CalculateDigest (digest, buf, 96); CryptoPP::Weak1::MD5().CalculateDigest (digest, (uint8_t *)buf, 96);
} }
} }
} }

2
i2p.cpp

@ -1,5 +1,5 @@
#include <thread> #include <thread>
#include <stdlib.h>
#include "Daemon.h" #include "Daemon.h"
int main( int argc, char* argv[] ) int main( int argc, char* argv[] )

Loading…
Cancel
Save