cumulative update from bitbucket

pull/295/head
orignal 9 years ago
parent 73d4025256
commit 62cf83921b

@ -7,8 +7,7 @@
#include <condition_variable> #include <condition_variable>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <cryptopp/osrng.h> #include "Base.h"
#include "base64.h"
#include "util.h" #include "util.h"
#include "Identity.h" #include "Identity.h"
#include "Log.h" #include "Log.h"
@ -26,8 +25,8 @@ namespace client
public: public:
AddressBookFilesystemStorage (); AddressBookFilesystemStorage ();
bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const; std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const i2p::data::IdentHash& ident) const;
void AddAddress (const i2p::data::IdentityEx& address); void AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
void RemoveAddress (const i2p::data::IdentHash& ident); void RemoveAddress (const i2p::data::IdentHash& ident);
int Load (std::map<std::string, i2p::data::IdentHash>& addresses); int Load (std::map<std::string, i2p::data::IdentHash>& addresses);
@ -50,7 +49,7 @@ namespace client
} }
} }
bool AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const std::shared_ptr<const i2p::data::IdentityEx> AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const
{ {
auto filename = GetPath () / (ident.ToBase32() + ".b32"); auto filename = GetPath () / (ident.ToBase32() + ".b32");
std::ifstream f(filename.c_str (), std::ifstream::binary); std::ifstream f(filename.c_str (), std::ifstream::binary);
@ -61,28 +60,28 @@ namespace client
if (len < i2p::data::DEFAULT_IDENTITY_SIZE) if (len < i2p::data::DEFAULT_IDENTITY_SIZE)
{ {
LogPrint (eLogError, "File ", filename, " is too short. ", len); LogPrint (eLogError, "File ", filename, " is too short. ", len);
return false; return nullptr;
} }
f.seekg(0, std::ios::beg); f.seekg(0, std::ios::beg);
uint8_t * buf = new uint8_t[len]; uint8_t * buf = new uint8_t[len];
f.read((char *)buf, len); f.read((char *)buf, len);
address.FromBuffer (buf, len); auto address = std::make_shared<i2p::data::IdentityEx>(buf, len);
delete[] buf; delete[] buf;
return true; return address;
} }
else else
return false; return nullptr;
} }
void AddressBookFilesystemStorage::AddAddress (const i2p::data::IdentityEx& address) void AddressBookFilesystemStorage::AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address)
{ {
auto filename = GetPath () / (address.GetIdentHash ().ToBase32() + ".b32"); auto filename = GetPath () / (address->GetIdentHash ().ToBase32() + ".b32");
std::ofstream f (filename.c_str (), std::ofstream::binary | std::ofstream::out); std::ofstream f (filename.c_str (), std::ofstream::binary | std::ofstream::out);
if (f.is_open ()) if (f.is_open ())
{ {
size_t len = address.GetFullLen (); size_t len = address->GetFullLen ();
uint8_t * buf = new uint8_t[len]; uint8_t * buf = new uint8_t[len];
address.ToBuffer (buf, len); address->ToBuffer (buf, len);
f.write ((char *)buf, len); f.write ((char *)buf, len);
delete[] buf; delete[] buf;
} }
@ -256,29 +255,29 @@ namespace client
void AddressBook::InsertAddress (const std::string& address, const std::string& base64) void AddressBook::InsertAddress (const std::string& address, const std::string& base64)
{ {
i2p::data::IdentityEx ident; auto ident = std::make_shared<i2p::data::IdentityEx>();
ident.FromBase64 (base64); ident->FromBase64 (base64);
if (!m_Storage) if (!m_Storage)
m_Storage = CreateStorage (); m_Storage = CreateStorage ();
m_Storage->AddAddress (ident); m_Storage->AddAddress (ident);
m_Addresses[address] = ident.GetIdentHash (); m_Addresses[address] = ident->GetIdentHash ();
LogPrint (address,"->", ToAddress(ident.GetIdentHash ()), " added"); LogPrint (address,"->", ToAddress(ident->GetIdentHash ()), " added");
} }
void AddressBook::InsertAddress (const i2p::data::IdentityEx& address) void AddressBook::InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address)
{ {
if (!m_Storage) if (!m_Storage)
m_Storage = CreateStorage (); m_Storage = CreateStorage ();
m_Storage->AddAddress (address); m_Storage->AddAddress (address);
} }
bool AddressBook::GetAddress (const std::string& address, i2p::data::IdentityEx& identity) std::shared_ptr<const i2p::data::IdentityEx> AddressBook::GetAddress (const std::string& address)
{ {
if (!m_Storage) if (!m_Storage)
m_Storage = CreateStorage (); m_Storage = CreateStorage ();
i2p::data::IdentHash ident; i2p::data::IdentHash ident;
if (!GetIdentHash (address, ident)) return false; if (!GetIdentHash (address, ident)) return nullptr;
return m_Storage->GetAddress (ident, identity); return m_Storage->GetAddress (ident);
} }
void AddressBook::LoadHosts () void AddressBook::LoadHosts ()
@ -332,10 +331,10 @@ namespace client
std::string name = s.substr(0, pos++); std::string name = s.substr(0, pos++);
std::string addr = s.substr(pos); std::string addr = s.substr(pos);
i2p::data::IdentityEx ident; auto ident = std::make_shared<i2p::data::IdentityEx> ();
if (ident.FromBase64(addr)) if (ident->FromBase64(addr))
{ {
m_Addresses[name] = ident.GetIdentHash (); m_Addresses[name] = ident->GetIdentHash ();
m_Storage->AddAddress (ident); m_Storage->AddAddress (ident);
numAddresses++; numAddresses++;
} }
@ -415,11 +414,10 @@ namespace client
{ {
auto dest = i2p::client::context.GetSharedLocalDestination (); auto dest = i2p::client::context.GetSharedLocalDestination ();
if (!dest) return; if (!dest) return;
if (m_IsLoaded && !m_IsDownloading && dest->IsReady ()) if (m_IsLoaded && !m_IsDownloading && dest->IsReady () && !m_Subscriptions.empty ())
{ {
// pick random subscription // pick random subscription
CryptoPP::AutoSeededRandomPool rnd; auto ind = rand () % m_Subscriptions.size();
auto ind = rnd.GenerateWord32 (0, m_Subscriptions.size() - 1);
m_IsDownloading = true; m_IsDownloading = true;
m_Subscriptions[ind]->CheckSubscription (); m_Subscriptions[ind]->CheckSubscription ();
} }

@ -7,8 +7,9 @@
#include <vector> #include <vector>
#include <iostream> #include <iostream>
#include <mutex> #include <mutex>
#include <memory>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "base64.h" #include "Base.h"
#include "util.h" #include "util.h"
#include "Identity.h" #include "Identity.h"
#include "Log.h" #include "Log.h"
@ -31,8 +32,8 @@ namespace client
public: public:
virtual ~AddressBookStorage () {}; virtual ~AddressBookStorage () {};
virtual bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const = 0; virtual std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const i2p::data::IdentHash& ident) const = 0;
virtual void AddAddress (const i2p::data::IdentityEx& address) = 0; virtual void AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address) = 0;
virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0; virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0;
virtual int Load (std::map<std::string, i2p::data::IdentHash>& addresses) = 0; virtual int Load (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
@ -49,16 +50,16 @@ namespace client
void Start (); void Start ();
void Stop (); void Stop ();
bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident); bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident);
bool GetAddress (const std::string& address, i2p::data::IdentityEx& identity); std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const std::string& address);
const i2p::data::IdentHash * FindAddress (const std::string& address); const i2p::data::IdentHash * FindAddress (const std::string& address);
void InsertAddress (const std::string& address, const std::string& base64); // for jump service void InsertAddress (const std::string& address, const std::string& base64); // for jump service
void InsertAddress (const i2p::data::IdentityEx& address); void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
void LoadHostsFromStream (std::istream& f); void LoadHostsFromStream (std::istream& f);
void DownloadComplete (bool success); void DownloadComplete (bool success);
//This method returns the ".b32.i2p" address //This method returns the ".b32.i2p" address
std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); } std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); }
std::string ToAddress(const i2p::data::IdentityEx& ident) { return ToAddress(ident.GetIdentHash ()); } std::string ToAddress(std::shared_ptr<const i2p::data::IdentityEx> ident) { return ToAddress(ident->GetIdentHash ()); }
private: private:
void StartSubscriptions (); void StartSubscriptions ();

@ -408,14 +408,14 @@ namespace client
{ {
LogPrint (eLogDebug, "BOB: newkeys"); LogPrint (eLogDebug, "BOB: newkeys");
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (); m_Keys = i2p::data::PrivateKeys::CreateRandomKeys ();
SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ()); SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
} }
void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len) void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len)
{ {
LogPrint (eLogDebug, "BOB: setkeys ", operand); LogPrint (eLogDebug, "BOB: setkeys ", operand);
m_Keys.FromBase64 (operand); m_Keys.FromBase64 (operand);
SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ()); SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
} }
void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len) void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len)
@ -427,7 +427,7 @@ namespace client
void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len) void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len)
{ {
LogPrint (eLogDebug, "BOB: getdest"); LogPrint (eLogDebug, "BOB: getdest");
SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ()); SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
} }
void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len) void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len)
@ -477,7 +477,7 @@ namespace client
auto localDestination = m_CurrentDestination->GetLocalDestination (); auto localDestination = m_CurrentDestination->GetLocalDestination ();
auto leaseSet = localDestination->FindLeaseSet (ident); auto leaseSet = localDestination->FindLeaseSet (ident);
if (leaseSet) if (leaseSet)
SendReplyOK (leaseSet->GetIdentity ().ToBase64 ().c_str ()); SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
else else
{ {
auto s = shared_from_this (); auto s = shared_from_this ();
@ -485,7 +485,7 @@ namespace client
[s](std::shared_ptr<i2p::data::LeaseSet> ls) [s](std::shared_ptr<i2p::data::LeaseSet> ls)
{ {
if (ls) if (ls)
s->SendReplyOK (ls->GetIdentity ().ToBase64 ().c_str ()); s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
else else
s->SendReplyError ("LeaseSet Not found"); s->SendReplyError ("LeaseSet Not found");
} }

@ -1,11 +1,11 @@
#include <stdlib.h> #include <stdlib.h>
#include "base64.h" #include "Log.h"
#include "Base.h"
namespace i2p namespace i2p
{ {
namespace data namespace data
{ {
static void iT64Build(void); static void iT64Build(void);
/* /*
@ -265,5 +265,69 @@ namespace data
} }
return ret; return ret;
} }
GzipInflator::GzipInflator (): m_IsDirty (false)
{
memset (&m_Inflator, 0, sizeof (m_Inflator));
inflateInit2 (&m_Inflator, MAX_WBITS + 16); // gzip
}
GzipInflator::~GzipInflator ()
{
inflateEnd (&m_Inflator);
}
size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
{
if (m_IsDirty) inflateReset (&m_Inflator);
m_IsDirty = true;
m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.avail_in = inLen;
m_Inflator.next_out = out;
m_Inflator.avail_out = outLen;
int err;
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END)
return outLen - m_Inflator.avail_out;
else
{
LogPrint (eLogError, "Decompression error ", err);
return 0;
}
}
GzipDeflator::GzipDeflator (): m_IsDirty (false)
{
memset (&m_Deflator, 0, sizeof (m_Deflator));
deflateInit2 (&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip
}
GzipDeflator::~GzipDeflator ()
{
deflateEnd (&m_Deflator);
}
void GzipDeflator::SetCompressionLevel (int level)
{
deflateParams (&m_Deflator, level, Z_DEFAULT_STRATEGY);
}
size_t GzipDeflator::Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
{
if (m_IsDirty) deflateReset (&m_Deflator);
m_IsDirty = true;
m_Deflator.next_in = const_cast<uint8_t *>(in);
m_Deflator.avail_in = inLen;
m_Deflator.next_out = out;
m_Deflator.avail_out = outLen;
int err;
if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END)
return outLen - m_Deflator.avail_out;
else
{
LogPrint (eLogError, "Compression error ", err);
return 0;
}
}
} }
} }

123
Base.h

@ -0,0 +1,123 @@
#ifndef BASE_H__
#define BASE_H__
#include <inttypes.h>
#include <string.h>
#include <string>
#include <zlib.h>
namespace i2p
{
namespace data
{
size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len);
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
const char * GetBase64SubstitutionTable ();
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen);
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
template<int sz>
class Tag
{
public:
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); };
Tag (const Tag<sz>& ) = default;
#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it
Tag (Tag<sz>&& ) = default;
#endif
Tag () = default;
Tag<sz>& operator= (const Tag<sz>& ) = default;
#ifndef _WIN32
Tag<sz>& operator= (Tag<sz>&& ) = default;
#endif
uint8_t * operator()() { return m_Buf; };
const uint8_t * operator()() const { return m_Buf; };
operator uint8_t * () { return m_Buf; };
operator const uint8_t * () const { return m_Buf; };
const uint64_t * GetLL () const { return ll; };
bool operator== (const Tag<sz>& other) const { return !memcmp (m_Buf, other.m_Buf, sz); };
bool operator< (const Tag<sz>& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; };
bool IsZero () const
{
for (int i = 0; i < sz/8; i++)
if (ll[i]) return false;
return true;
}
std::string ToBase64 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
std::string ToBase32 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
void FromBase32 (const std::string& s)
{
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
void FromBase64 (const std::string& s)
{
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
private:
union // 8 bytes alignment
{
uint8_t m_Buf[sz];
uint64_t ll[sz/8];
};
};
class GzipInflator
{
public:
GzipInflator ();
~GzipInflator ();
size_t Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
private:
z_stream m_Inflator;
bool m_IsDirty;
};
class GzipDeflator
{
public:
GzipDeflator ();
~GzipDeflator ();
void SetCompressionLevel (int level);
size_t Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
private:
z_stream m_Deflator;
bool m_IsDirty;
};
}
}
#endif

@ -15,7 +15,7 @@ namespace client
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr), ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr), m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr),
m_BOBCommandChannel (nullptr), m_I2PControlService (nullptr) m_BOBCommandChannel (nullptr)
{ {
} }
@ -25,7 +25,6 @@ namespace client
delete m_SocksProxy; delete m_SocksProxy;
delete m_SamBridge; delete m_SamBridge;
delete m_BOBCommandChannel; delete m_BOBCommandChannel;
delete m_I2PControlService;
} }
void ClientContext::Start () void ClientContext::Start ()
@ -33,7 +32,7 @@ namespace client
if (!m_SharedLocalDestination) if (!m_SharedLocalDestination)
{ {
m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA
m_Destinations[m_SharedLocalDestination->GetIdentity ().GetIdentHash ()] = m_SharedLocalDestination; m_Destinations[m_SharedLocalDestination->GetIdentity ()->GetIdentHash ()] = m_SharedLocalDestination;
m_SharedLocalDestination->Start (); m_SharedLocalDestination->Start ();
} }
@ -93,14 +92,6 @@ namespace client
LogPrint("BOB command channel started"); LogPrint("BOB command channel started");
} }
// I2P Control
int i2pcontrolPort = i2p::util::config::GetArg("-i2pcontrolport", 0);
if (i2pcontrolPort)
{
m_I2PControlService = new I2PControlService (i2pcontrolPort);
m_I2PControlService->Start ();
LogPrint("I2PControl started");
}
m_AddressBook.Start (); m_AddressBook.Start ();
} }
@ -140,13 +131,6 @@ namespace client
m_BOBCommandChannel = nullptr; m_BOBCommandChannel = nullptr;
LogPrint("BOB command channel stopped"); LogPrint("BOB command channel stopped");
} }
if (m_I2PControlService)
{
m_I2PControlService->Stop ();
delete m_I2PControlService;
m_I2PControlService = nullptr;
LogPrint("I2PControl stopped");
}
m_AddressBook.Stop (); m_AddressBook.Stop ();
for (auto it: m_Destinations) for (auto it: m_Destinations)
it.second->Stop (); it.second->Stop ();
@ -168,7 +152,7 @@ namespace client
s.read ((char *)buf, len); s.read ((char *)buf, len);
keys.FromBuffer (buf, len); keys.FromBuffer (buf, len);
delete[] buf; delete[] buf;
LogPrint ("Local address ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " loaded"); LogPrint ("Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded");
} }
else else
{ {
@ -181,15 +165,15 @@ namespace client
f.write ((char *)buf, len); f.write ((char *)buf, len);
delete[] buf; delete[] buf;
LogPrint ("New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " created"); LogPrint ("New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
} }
std::shared_ptr<ClientDestination> localDestination = nullptr; std::shared_ptr<ClientDestination> localDestination = nullptr;
std::unique_lock<std::mutex> l(m_DestinationsMutex); std::unique_lock<std::mutex> l(m_DestinationsMutex);
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ()); auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ());
if (it != m_Destinations.end ()) if (it != m_Destinations.end ())
{ {
LogPrint (eLogWarning, "Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " alreday exists"); LogPrint (eLogWarning, "Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " alreday exists");
localDestination = it->second; localDestination = it->second;
} }
else else
@ -230,10 +214,10 @@ namespace client
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
const std::map<std::string, std::string> * params) const std::map<std::string, std::string> * params)
{ {
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ()); auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ());
if (it != m_Destinations.end ()) if (it != m_Destinations.end ())
{ {
LogPrint ("Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " exists"); LogPrint ("Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists");
if (!it->second->IsRunning ()) if (!it->second->IsRunning ())
{ {
it->second->Start (); it->second->Start ();
@ -243,7 +227,7 @@ namespace client
} }
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params); auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
std::unique_lock<std::mutex> l(m_DestinationsMutex); std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[keys.GetPublic ().GetIdentHash ()] = localDestination; m_Destinations[keys.GetPublic ()->GetIdentHash ()] = localDestination;
localDestination->Start (); localDestination->Start ();
return localDestination; return localDestination;
} }

@ -11,7 +11,6 @@
#include "SAM.h" #include "SAM.h"
#include "BOB.h" #include "BOB.h"
#include "AddressBook.h" #include "AddressBook.h"
#include "I2PControl.h"
namespace i2p namespace i2p
{ {
@ -72,7 +71,6 @@ namespace client
std::map<i2p::data::IdentHash, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // destination->tunnel std::map<i2p::data::IdentHash, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // destination->tunnel
SAMBridge * m_SamBridge; SAMBridge * m_SamBridge;
BOBCommandChannel * m_BOBCommandChannel; BOBCommandChannel * m_BOBCommandChannel;
I2PControlService * m_I2PControlService;
public: public:
// for HTTP // for HTTP

@ -1,13 +1,275 @@
#include <stdlib.h> #include <string.h>
#include "TunnelBase.h" #include <string>
#include "aes.h" #include <openssl/sha.h>
#include <openssl/dh.h>
#include <openssl/md5.h>
#include <openssl/rand.h>
#include "Log.h"
//#include "TunnelBase.h"
#include "Crypto.h"
namespace i2p namespace i2p
{ {
namespace crypto 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
};
#ifdef AESNI const int 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 int rsae_ = 65537;
const CryptoConstants& GetCryptoConstants ()
{
static CryptoConstants cryptoConstants (elgp_, elgg_, dsap_, dsaq_, dsag_, rsae_);
return cryptoConstants;
}
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len)
{
int offset = len - BN_num_bytes (bn);
if (offset < 0) return false;
BN_bn2bin (bn, buf + offset);
memset (buf, 0, offset);
return true;
}
// DH
DHKeys::DHKeys (): m_IsUpdated (true)
{
m_DH = DH_new ();
m_DH->p = BN_dup (elgp);
m_DH->g = BN_dup (elgg);
m_DH->priv_key = NULL;
m_DH->pub_key = NULL;
}
DHKeys::~DHKeys ()
{
DH_free (m_DH);
}
void DHKeys::GenerateKeys (uint8_t * priv, uint8_t * pub)
{
if (m_DH->priv_key) { BN_free (m_DH->priv_key); m_DH->priv_key = NULL; };
if (m_DH->pub_key) { BN_free (m_DH->pub_key); m_DH->pub_key = NULL; };
DH_generate_key (m_DH);
if (priv) bn2buf (m_DH->priv_key, priv, 256);
if (pub) bn2buf (m_DH->pub_key, pub, 256);
m_IsUpdated = true;
}
const uint8_t * DHKeys::GetPublicKey ()
{
if (m_IsUpdated)
{
bn2buf (m_DH->pub_key, m_PublicKey, 256);
BN_free (m_DH->pub_key); m_DH->pub_key = NULL;
m_IsUpdated= false;
}
return m_PublicKey;
}
void DHKeys::Agree (const uint8_t * pub, uint8_t * shared)
{
BIGNUM * pk = BN_bin2bn (pub, 256, NULL);
DH_compute_key (shared, pk, m_DH);
BN_free (pk);
}
// ElGamal
ElGamalEncryption::ElGamalEncryption (const uint8_t * key)
{
ctx = BN_CTX_new ();
// select random k
BIGNUM * k = BN_new ();
BN_rand_range (k, elgp);
if (BN_is_zero (k)) BN_one (k);
// caulculate a
a = BN_new ();
BN_mod_exp (a, elgg, k, elgp, ctx);
BIGNUM * y = BN_new ();
BN_bin2bn (key, 256, y);
// calculate b1
b1 = BN_new ();
BN_mod_exp (b1, y, k, elgp, ctx);
BN_free (y);
BN_free (k);
}
ElGamalEncryption::~ElGamalEncryption ()
{
BN_CTX_free (ctx);
BN_free (a);
BN_free (b1);
}
void ElGamalEncryption::Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding) const
{
// create m
uint8_t m[255];
m[0] = 0xFF;
memcpy (m+33, data, len);
SHA256 (m+33, 222, m+1);
// calculate b = b1*m mod p
BIGNUM * b = BN_new ();
BN_bin2bn (m, 255, b);
BN_mod_mul (b, b1, b, elgp, ctx);
// copy a and b
if (zeroPadding)
{
encrypted[0] = 0;
bn2buf (a, encrypted + 1, 256);
encrypted[257] = 0;
bn2buf (b, encrypted + 258, 256);
}
else
{
bn2buf (a, encrypted, 256);
bn2buf (b, encrypted + 256, 256);
}
BN_free (b);
}
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
uint8_t * data, bool zeroPadding)
{
BN_CTX * ctx = BN_CTX_new ();
BIGNUM * x = BN_new (), * a = BN_new (), * b = BN_new ();
BN_bin2bn (key, 256, x);
BN_sub (x, elgp, x); BN_sub_word (x, 1); // x = elgp - x- 1
BN_bin2bn (zeroPadding ? encrypted + 1 : encrypted, 256, a);
BN_bin2bn (zeroPadding ? encrypted + 258 : encrypted + 256, 256, b);
// m = b*(a^x mod p) mod p
BN_mod_exp (x, a, x, elgp, ctx);
BN_mod_mul (b, b, x, elgp, ctx);
uint8_t m[255];
bn2buf (b, m, 255);
BN_free (x); BN_free (a); BN_free (b);
BN_CTX_free (ctx);
uint8_t hash[32];
SHA256 (m + 33, 222, hash);
if (memcmp (m + 1, hash, 32))
{
LogPrint (eLogError, "ElGamal decrypt hash doesn't match");
return false;
}
memcpy (data, m + 33, 222);
return true;
}
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub)
{
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
RAND_bytes (priv, 256);
BN_CTX * ctx = BN_CTX_new ();
BIGNUM * p = BN_new ();
BN_bin2bn (priv, 256, p);
BN_mod_exp (p, elgg, p, elgp, ctx);
bn2buf (p, pub, 256);
BN_free (p);
BN_CTX_free (ctx);
#else
DHKeys dh;
dh.GenerateKeys (priv, pub);
#endif
}
// HMAC
const uint64_t IPAD = 0x3636363636363636;
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest)
// key is 32 bytes
// digest is 16 bytes
// block size is 64 bytes
{
uint64_t buf[256];
// ikeypad
buf[0] = key.GetLL ()[0] ^ IPAD;
buf[1] = key.GetLL ()[1] ^ IPAD;
buf[2] = key.GetLL ()[2] ^ IPAD;
buf[3] = key.GetLL ()[3] ^ IPAD;
buf[4] = IPAD;
buf[5] = IPAD;
buf[6] = IPAD;
buf[7] = IPAD;
// concatenate with msg
memcpy (buf + 8, msg, len);
// calculate first hash
uint8_t hash[16]; // MD5
MD5((uint8_t *)buf, len + 64, hash);
// okeypad
buf[0] = key.GetLL ()[0] ^ OPAD;
buf[1] = key.GetLL ()[1] ^ OPAD;
buf[2] = key.GetLL ()[2] ^ OPAD;
buf[3] = key.GetLL ()[3] ^ OPAD;
buf[4] = OPAD;
buf[5] = OPAD;
buf[6] = OPAD;
buf[7] = OPAD;
// copy first hash after okeypad
memcpy (buf + 8, hash, 16);
// fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P)
memset (buf + 10, 0, 16);
// calculate digest
MD5((uint8_t *)buf, 96, digest);
}
// AES
#ifdef AESNI
#define KeyExpansion256(round0,round1) \ #define KeyExpansion256(round0,round1) \
"pshufd $0xff, %%xmm2, %%xmm2 \n" \ "pshufd $0xff, %%xmm2, %%xmm2 \n" \
@ -311,7 +573,7 @@ namespace crypto
#else #else
m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
m_LayerEncryption.SetIV (out); m_LayerEncryption.SetIV (out);
m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data m_LayerEncryption.Encrypt (in + 16, /*i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE*/1008, out + 16); // data
m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
#endif #endif
} }
@ -348,10 +610,10 @@ namespace crypto
#else #else
m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
m_LayerDecryption.SetIV (out); m_LayerDecryption.SetIV (out);
m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data m_LayerDecryption.Decrypt (in + 16, /*i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE*/1008, out + 16); // data
m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
#endif #endif
} }
} }
} }

@ -1,15 +1,113 @@
#ifndef AES_H__ #ifndef CRYPTO_H__
#define AES_H__ #define CRYPTO_H__
#include <inttypes.h> #include <inttypes.h>
#include <cryptopp/modes.h> #include <string>
#include <cryptopp/aes.h> #include <openssl/bn.h>
#include "Identity.h" #include <openssl/dh.h>
#include <openssl/aes.h>
#include "Base.h"
namespace i2p namespace i2p
{ {
namespace crypto namespace crypto
{ {
struct CryptoConstants
{
// DH/ElGamal
BIGNUM * elgp;
BIGNUM * elgg;
// DSA
BIGNUM * dsap;
BIGNUM * dsaq;
BIGNUM * dsag;
// RSA
BIGNUM * rsae;
CryptoConstants (const uint8_t * elgp_, int elgg_, const uint8_t * dsap_,
const uint8_t * dsaq_, const uint8_t * dsag_, int rsae_)
{
elgp = BN_new ();
BN_bin2bn (elgp_, 256, elgp);
elgg = BN_new ();
BN_set_word (elgg, elgg_);
dsap = BN_new ();
BN_bin2bn (dsap_, 128, dsap);
dsaq = BN_new ();
BN_bin2bn (dsaq_, 20, dsaq);
dsag = BN_new ();
BN_bin2bn (dsag_, 128, dsag);
rsae = BN_new ();
BN_set_word (rsae, rsae_);
}
~CryptoConstants ()
{
BN_free (elgp); BN_free (elgg); BN_free (dsap); BN_free (dsaq); BN_free (dsag); BN_free (rsae);
}
};
const CryptoConstants& GetCryptoConstants ();
// DH/ElGamal
#define elgp GetCryptoConstants ().elgp
#define elgg GetCryptoConstants ().elgg
// DSA
#define dsap GetCryptoConstants ().dsap
#define dsaq GetCryptoConstants ().dsaq
#define dsag GetCryptoConstants ().dsag
// RSA
#define rsae GetCryptoConstants ().rsae
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len);
// DH
class DHKeys
{
public:
DHKeys ();
~DHKeys ();
void GenerateKeys (uint8_t * priv = nullptr, uint8_t * pub = nullptr);
const uint8_t * GetPublicKey ();
void Agree (const uint8_t * pub, uint8_t * shared);
private:
DH * m_DH;
uint8_t m_PublicKey[256];
bool m_IsUpdated;
};
// ElGamal
class ElGamalEncryption
{
public:
ElGamalEncryption (const uint8_t * key);
~ElGamalEncryption ();
void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false) const;
private:
BN_CTX * ctx;
BIGNUM * a, * b1;
};
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, bool zeroPadding = false);
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub);
// HMAC
typedef i2p::data::Tag<32> MACKey;
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest);
// AES
struct ChipherBlock struct ChipherBlock
{ {
uint8_t buf[16]; uint8_t buf[16];
@ -95,7 +193,7 @@ namespace crypto
typedef ECBEncryptionAESNI ECBEncryption; typedef ECBEncryptionAESNI ECBEncryption;
typedef ECBDecryptionAESNI ECBDecryption; typedef ECBDecryptionAESNI ECBDecryption;
#else // use crypto++ #else // use openssl
class ECBEncryption class ECBEncryption
{ {
@ -103,16 +201,16 @@ namespace crypto
void SetKey (const AESKey& key) void SetKey (const AESKey& key)
{ {
m_Encryption.SetKey (key, 32); AES_set_encrypt_key (key, 256, &m_Key);
} }
void Encrypt (const ChipherBlock * in, ChipherBlock * out) void Encrypt (const ChipherBlock * in, ChipherBlock * out)
{ {
m_Encryption.ProcessData (out->buf, in->buf, 16); AES_encrypt (in->buf, out->buf, &m_Key);
} }
private: private:
CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_Encryption; AES_KEY m_Key;
}; };
class ECBDecryption class ECBDecryption
@ -121,16 +219,16 @@ namespace crypto
void SetKey (const AESKey& key) void SetKey (const AESKey& key)
{ {
m_Decryption.SetKey (key, 32); AES_set_decrypt_key (key, 256, &m_Key);
} }
void Decrypt (const ChipherBlock * in, ChipherBlock * out) void Decrypt (const ChipherBlock * in, ChipherBlock * out)
{ {
m_Decryption.ProcessData (out->buf, in->buf, 16); AES_decrypt (in->buf, out->buf, &m_Key);
} }
private: private:
CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption m_Decryption; AES_KEY m_Key;
}; };
@ -217,9 +315,8 @@ namespace crypto
#else #else
CBCDecryption m_LayerDecryption; CBCDecryption m_LayerDecryption;
#endif #endif
}; };
} }
} }
#endif #endif

@ -1,9 +1,11 @@
#include <thread> #include <thread>
#include <memory>
#include <openssl/ssl.h>
#include "Daemon.h" #include "Daemon.h"
#include "Log.h" #include "Log.h"
#include "base64.h" #include "Base.h"
#include "version.h" #include "version.h"
#include "Transports.h" #include "Transports.h"
#include "NTCPSession.h" #include "NTCPSession.h"
@ -16,8 +18,13 @@
#include "Streaming.h" #include "Streaming.h"
#include "Destination.h" #include "Destination.h"
#include "HTTPServer.h" #include "HTTPServer.h"
#include "I2PControl.h"
#include "ClientContext.h" #include "ClientContext.h"
#ifdef USE_UPNP
#include "UPnP.h"
#endif
namespace i2p namespace i2p
{ {
namespace util namespace util
@ -25,14 +32,15 @@ namespace i2p
class Daemon_Singleton::Daemon_Singleton_Private class Daemon_Singleton::Daemon_Singleton_Private
{ {
public: public:
Daemon_Singleton_Private() : httpServer(nullptr) Daemon_Singleton_Private() {};
{}; ~Daemon_Singleton_Private() {};
~Daemon_Singleton_Private()
{
delete httpServer;
};
i2p::util::HTTPServer *httpServer; std::unique_ptr<i2p::util::HTTPServer> httpServer;
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
#ifdef USE_UPNP
UPnP m_UPnP;
#endif
}; };
Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {}; Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {};
@ -51,6 +59,7 @@ namespace i2p
bool Daemon_Singleton::init(int argc, char* argv[]) bool Daemon_Singleton::init(int argc, char* argv[])
{ {
SSL_library_init ();
i2p::util::config::OptionParser(argc, argv); i2p::util::config::OptionParser(argc, argv);
i2p::context.Init (); i2p::context.Init ();
@ -106,17 +115,29 @@ namespace i2p
StartLog (""); // write to stdout StartLog (""); // write to stdout
} }
d.httpServer = new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpport", 7070)); d.httpServer = std::unique_ptr<i2p::util::HTTPServer>(new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpport", 7070)));
d.httpServer->Start(); d.httpServer->Start();
LogPrint("HTTP Server started"); LogPrint("HTTP Server started");
i2p::data::netdb.Start(); i2p::data::netdb.Start();
LogPrint("NetDB started"); LogPrint("NetDB started");
#ifdef USE_UPNP
d.m_UPnP.Start ();
LogPrint(eLogInfo, "UPnP started");
#endif
i2p::transport::transports.Start(); i2p::transport::transports.Start();
LogPrint("Transports started"); LogPrint("Transports started");
i2p::tunnel::tunnels.Start(); i2p::tunnel::tunnels.Start();
LogPrint("Tunnels started"); LogPrint("Tunnels started");
i2p::client::context.Start (); i2p::client::context.Start ();
LogPrint("Client started"); LogPrint("Client started");
// I2P Control
int i2pcontrolPort = i2p::util::config::GetArg("-i2pcontrolport", 0);
if (i2pcontrolPort)
{
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcontrolPort));
d.m_I2PControlService->Start ();
LogPrint("I2PControl started");
}
return true; return true;
} }
@ -127,17 +148,25 @@ namespace i2p
LogPrint("Client stopped"); LogPrint("Client stopped");
i2p::tunnel::tunnels.Stop(); i2p::tunnel::tunnels.Stop();
LogPrint("Tunnels stopped"); LogPrint("Tunnels stopped");
#ifdef USE_UPNP
d.m_UPnP.Stop ();
LogPrint(eLogInfo, "UPnP stopped");
#endif
i2p::transport::transports.Stop(); i2p::transport::transports.Stop();
LogPrint("Transports stopped"); LogPrint("Transports stopped");
i2p::data::netdb.Stop(); i2p::data::netdb.Stop();
LogPrint("NetDB stopped"); LogPrint("NetDB stopped");
d.httpServer->Stop(); d.httpServer->Stop();
d.httpServer = nullptr;
LogPrint("HTTP Server stopped"); LogPrint("HTTP Server stopped");
if (d.m_I2PControlService)
{
d.m_I2PControlService->Stop ();
d.m_I2PControlService = nullptr;
LogPrint("I2PControl stopped");
}
StopLog (); StopLog ();
delete d.httpServer; d.httpServer = nullptr;
return true; return true;
} }
} }

@ -1,7 +1,7 @@
#include <string.h> #include <string.h>
#include <vector> #include <vector>
#include <cryptopp/sha.h> #include <openssl/sha.h>
#include <cryptopp/gzip.h> #include <openssl/rand.h>
#include "Log.h" #include "Log.h"
#include "TunnelBase.h" #include "TunnelBase.h"
#include "RouterContext.h" #include "RouterContext.h"
@ -12,36 +12,40 @@ namespace i2p
{ {
namespace datagram namespace datagram
{ {
DatagramDestination::DatagramDestination (i2p::client::ClientDestination& owner): DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
m_Owner (owner), m_Receiver (nullptr) m_Owner (owner), m_Receiver (nullptr)
{ {
} }
DatagramDestination::~DatagramDestination ()
{
}
void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort, uint16_t toPort) void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort, uint16_t toPort)
{ {
uint8_t buf[MAX_DATAGRAM_SIZE]; uint8_t buf[MAX_DATAGRAM_SIZE];
auto identityLen = m_Owner.GetIdentity ().ToBuffer (buf, MAX_DATAGRAM_SIZE); auto identityLen = m_Owner->GetIdentity ()->ToBuffer (buf, MAX_DATAGRAM_SIZE);
uint8_t * signature = buf + identityLen; uint8_t * signature = buf + identityLen;
auto signatureLen = m_Owner.GetIdentity ().GetSignatureLen (); auto signatureLen = m_Owner->GetIdentity ()->GetSignatureLen ();
uint8_t * buf1 = signature + signatureLen; uint8_t * buf1 = signature + signatureLen;
size_t headerLen = identityLen + signatureLen; size_t headerLen = identityLen + signatureLen;
memcpy (buf1, payload, len); memcpy (buf1, payload, len);
if (m_Owner.GetIdentity ().GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{ {
uint8_t hash[32]; uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest (hash, buf1, len); SHA256(buf1, len, hash);
m_Owner.Sign (hash, 32, signature); m_Owner->Sign (hash, 32, signature);
} }
else else
m_Owner.Sign (buf1, len, signature); m_Owner->Sign (buf1, len, signature);
auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort); auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
auto remote = m_Owner.FindLeaseSet (ident); auto remote = m_Owner->FindLeaseSet (ident);
if (remote) if (remote)
m_Owner.GetService ().post (std::bind (&DatagramDestination::SendMsg, this, msg, remote)); m_Owner->GetService ().post (std::bind (&DatagramDestination::SendMsg, this, msg, remote));
else else
m_Owner.RequestDestination (ident, std::bind (&DatagramDestination::HandleLeaseSetRequestComplete, this, std::placeholders::_1, msg)); m_Owner->RequestDestination (ident, std::bind (&DatagramDestination::HandleLeaseSetRequestComplete, this, std::placeholders::_1, msg));
} }
void DatagramDestination::HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> remote, I2NPMessage * msg) void DatagramDestination::HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> remote, I2NPMessage * msg)
@ -54,13 +58,13 @@ namespace datagram
void DatagramDestination::SendMsg (I2NPMessage * msg, std::shared_ptr<const i2p::data::LeaseSet> remote) void DatagramDestination::SendMsg (I2NPMessage * msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
{ {
auto outboundTunnel = m_Owner.GetTunnelPool ()->GetNextOutboundTunnel (); auto outboundTunnel = m_Owner->GetTunnelPool ()->GetNextOutboundTunnel ();
auto leases = remote->GetNonExpiredLeases (); auto leases = remote->GetNonExpiredLeases ();
if (!leases.empty () && outboundTunnel) if (!leases.empty () && outboundTunnel)
{ {
std::vector<i2p::tunnel::TunnelMessageBlock> msgs; std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1); uint32_t i = rand () % leases.size ();
auto garlic = m_Owner.WrapMessage (remote, ToSharedI2NPMessage (msg), true); auto garlic = m_Owner->WrapMessage (remote, ToSharedI2NPMessage (msg), true);
msgs.push_back (i2p::tunnel::TunnelMessageBlock msgs.push_back (i2p::tunnel::TunnelMessageBlock
{ {
i2p::tunnel::eDeliveryTypeTunnel, i2p::tunnel::eDeliveryTypeTunnel,
@ -90,7 +94,7 @@ namespace datagram
if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{ {
uint8_t hash[32]; uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest (hash, buf + headerLen, len - headerLen); SHA256(buf + headerLen, len - headerLen, hash);
verified = identity.Verify (hash, 32, signature); verified = identity.Verify (hash, 32, signature);
} }
else else
@ -113,37 +117,32 @@ namespace datagram
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{ {
// unzip it // unzip it
CryptoPP::Gunzip decompressor;
decompressor.Put (buf, len);
decompressor.MessageEnd();
uint8_t uncompressed[MAX_DATAGRAM_SIZE]; uint8_t uncompressed[MAX_DATAGRAM_SIZE];
auto uncompressedLen = decompressor.MaxRetrievable (); size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
if (uncompressedLen <= MAX_DATAGRAM_SIZE) if (uncompressedLen)
{
decompressor.Get (uncompressed, uncompressedLen);
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen); HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
}
else
LogPrint ("Received datagram size ", uncompressedLen, " exceeds max size");
} }
I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort) I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
{ {
I2NPMessage * msg = NewI2NPMessage (); I2NPMessage * msg = NewI2NPMessage ();
CryptoPP::Gzip compressor; // default level
compressor.Put (payload, len);
compressor.MessageEnd();
int size = compressor.MaxRetrievable ();
uint8_t * buf = msg->GetPayload (); uint8_t * buf = msg->GetPayload ();
htobe32buf (buf, size); // length buf += 4; // reserve for length
buf += 4; size_t size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
compressor.Get (buf, size); if (size)
htobe16buf (buf + 4, fromPort); // source port {
htobe16buf (buf + 6, toPort); // destination port htobe32buf (msg->GetPayload (), size); // length
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol htobe16buf (buf + 4, fromPort); // source port
msg->len += size + 4; htobe16buf (buf + 6, toPort); // destination port
msg->FillI2NPMessageHeader (eI2NPData); buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
msg->len += size + 4;
msg->FillI2NPMessageHeader (eI2NPData);
}
else
{
DeleteI2NPMessage (msg);
msg = nullptr;
}
return msg; return msg;
} }
} }

@ -5,6 +5,7 @@
#include <memory> #include <memory>
#include <functional> #include <functional>
#include <map> #include <map>
#include "Base.h"
#include "Identity.h" #include "Identity.h"
#include "LeaseSet.h" #include "LeaseSet.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
@ -24,8 +25,8 @@ namespace datagram
public: public:
DatagramDestination (i2p::client::ClientDestination& owner); DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
~DatagramDestination () {}; ~DatagramDestination ();
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0); void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
@ -46,9 +47,12 @@ namespace datagram
private: private:
i2p::client::ClientDestination& m_Owner; std::shared_ptr<i2p::client::ClientDestination> m_Owner;
Receiver m_Receiver; // default Receiver m_Receiver; // default
std::map<uint16_t, Receiver> m_ReceiversByPorts; std::map<uint16_t, Receiver> m_ReceiversByPorts;
i2p::data::GzipInflator m_Inflator;
i2p::data::GzipDeflator m_Deflator;
}; };
} }
} }

@ -1,12 +1,12 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <openssl/rand.h>
#include "Log.h" #include "Log.h"
#include "util.h" #include "util.h"
#include "ElGamal.h" #include "Crypto.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "NetDb.h" #include "NetDb.h"
#include "AddressBook.h"
#include "Destination.h" #include "Destination.h"
namespace i2p namespace i2p
@ -19,7 +19,7 @@ namespace client
m_Keys (keys), m_IsPublic (isPublic), m_PublishReplyToken (0), m_Keys (keys), m_IsPublic (isPublic), m_PublishReplyToken (0),
m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service), m_CleanupTimer (m_Service) m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service), m_CleanupTimer (m_Service)
{ {
i2p::crypto::GenerateElGamalKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey); i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH; int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH; int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY; int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY;
@ -86,8 +86,7 @@ namespace client
if (explicitPeers) if (explicitPeers)
m_Pool->SetExplicitPeers (explicitPeers); m_Pool->SetExplicitPeers (explicitPeers);
if (m_IsPublic) if (m_IsPublic)
LogPrint (eLogInfo, "Local address ", i2p::client::GetB32Address(GetIdentHash()), " created"); LogPrint (eLogInfo, "Local address ", GetIdentHash().ToBase32 (), " created");
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (*this); // TODO:
} }
ClientDestination::~ClientDestination () ClientDestination::~ClientDestination ()
@ -123,8 +122,9 @@ namespace client
{ {
m_IsRunning = true; m_IsRunning = true;
m_Pool->SetLocalDestination (this); m_Pool->SetLocalDestination (this);
m_Pool->SetActive (true); m_Pool->SetActive (true);
m_Thread = new std::thread (std::bind (&ClientDestination::Run, this)); m_Thread = new std::thread (std::bind (&ClientDestination::Run, this));
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (shared_from_this ()); // TODO:
m_StreamingDestination->Start (); m_StreamingDestination->Start ();
for (auto it: m_StreamingDestinationsByPorts) for (auto it: m_StreamingDestinationsByPorts)
it.second->Start (); it.second->Start ();
@ -141,7 +141,8 @@ namespace client
{ {
m_CleanupTimer.cancel (); m_CleanupTimer.cancel ();
m_IsRunning = false; m_IsRunning = false;
m_StreamingDestination->Stop (); m_StreamingDestination->Stop ();
m_StreamingDestination = nullptr;
for (auto it: m_StreamingDestinationsByPorts) for (auto it: m_StreamingDestinationsByPorts)
it.second->Stop (); it.second->Stop ();
if (m_DatagramDestination) if (m_DatagramDestination)
@ -396,8 +397,8 @@ namespace client
} }
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
LogPrint (eLogDebug, "Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); LogPrint (eLogDebug, "Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
m_PublishReplyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
auto msg = WrapMessage (floodfill, ToSharedI2NPMessage (i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken))); auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken));
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer, m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer,
this, std::placeholders::_1)); this, std::placeholders::_1));
@ -507,7 +508,7 @@ namespace client
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port) std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port)
{ {
auto dest = std::make_shared<i2p::stream::StreamingDestination> (*this, port); auto dest = std::make_shared<i2p::stream::StreamingDestination> (shared_from_this (), port);
if (port) if (port)
m_StreamingDestinationsByPorts[port] = dest; m_StreamingDestinationsByPorts[port] = dest;
else // update default else // update default
@ -518,7 +519,7 @@ namespace client
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination () i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
{ {
if (!m_DatagramDestination) if (!m_DatagramDestination)
m_DatagramDestination = new i2p::datagram::DatagramDestination (*this); m_DatagramDestination = new i2p::datagram::DatagramDestination (shared_from_this ());
return m_DatagramDestination; return m_DatagramDestination;
} }
@ -529,7 +530,7 @@ namespace client
if (requestComplete) requestComplete (false); if (requestComplete) requestComplete (false);
return false; return false;
} }
m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, this, dest, requestComplete)); m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
return true; return true;
} }
@ -579,15 +580,14 @@ namespace client
request->requestTime = i2p::util::GetSecondsSinceEpoch (); request->requestTime = i2p::util::GetSecondsSinceEpoch ();
request->requestTimeoutTimer.cancel (); request->requestTimeoutTimer.cancel ();
CryptoPP::AutoSeededRandomPool rnd;
uint8_t replyKey[32], replyTag[32]; uint8_t replyKey[32], replyTag[32];
rnd.GenerateBlock (replyKey, 32); // random session key RAND_bytes (replyKey, 32); // random session key
rnd.GenerateBlock (replyTag, 32); // random session tag RAND_bytes (replyTag, 32); // random session tag
AddSessionKey (replyKey, replyTag); AddSessionKey (replyKey, replyTag);
auto msg = WrapMessage (nextFloodfill, auto msg = WrapMessage (nextFloodfill,
ToSharedI2NPMessage (CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
replyTunnel.get (), replyKey, replyTag))); replyTunnel.get (), replyKey, replyTag));
outboundTunnel->SendTunnelDataMsg ( outboundTunnel->SendTunnelDataMsg (
{ {
i2p::tunnel::TunnelMessageBlock i2p::tunnel::TunnelMessageBlock
@ -646,7 +646,7 @@ namespace client
CleanupRemoteLeaseSets (); CleanupRemoteLeaseSets ();
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer, m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
this, std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
} }
} }

@ -11,7 +11,7 @@
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Identity.h" #include "Identity.h"
#include "TunnelPool.h" #include "TunnelPool.h"
#include "CryptoConst.h" #include "Crypto.h"
#include "LeaseSet.h" #include "LeaseSet.h"
#include "Garlic.h" #include "Garlic.h"
#include "NetDb.h" #include "NetDb.h"
@ -45,7 +45,8 @@ namespace client
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete; typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
class ClientDestination: public i2p::garlic::GarlicDestination class ClientDestination: public i2p::garlic::GarlicDestination,
public std::enable_shared_from_this<ClientDestination>
{ {
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete; typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
// leaseSet = nullptr means not found // leaseSet = nullptr means not found

@ -1,87 +0,0 @@
#ifndef EL_GAMAL_H__
#define EL_GAMAL_H__
#include <inttypes.h>
#include <cryptopp/integer.h>
#include <cryptopp/osrng.h>
#include <cryptopp/dh.h>
#include <cryptopp/sha.h>
#include "CryptoConst.h"
#include "Log.h"
namespace i2p
{
namespace crypto
{
class ElGamalEncryption
{
public:
ElGamalEncryption (const uint8_t * key)
{
CryptoPP::AutoSeededRandomPool rnd;
CryptoPP::Integer y (key, 256), k (rnd, CryptoPP::Integer::One(), elgp-1);
a = a_exp_b_mod_c (elgg, k, elgp);
b1 = a_exp_b_mod_c (y, k, elgp);
}
void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false) const
{
// calculate b = b1*m mod p
uint8_t m[255];
m[0] = 0xFF;
memcpy (m+33, data, len);
CryptoPP::SHA256().CalculateDigest(m+1, m+33, 222);
CryptoPP::Integer b (a_times_b_mod_c (b1, CryptoPP::Integer (m, 255), elgp));
// copy a and b
if (zeroPadding)
{
encrypted[0] = 0;
a.Encode (encrypted + 1, 256);
encrypted[257] = 0;
b.Encode (encrypted + 258, 256);
}
else
{
a.Encode (encrypted, 256);
b.Encode (encrypted + 256, 256);
}
}
private:
CryptoPP::Integer a, b1;
};
inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
uint8_t * data, bool zeroPadding = false)
{
CryptoPP::Integer x(key, 256), a(zeroPadding? encrypted +1 : encrypted, 256),
b(zeroPadding? encrypted + 258 :encrypted + 256, 256);
uint8_t m[255];
a_times_b_mod_c (b, a_exp_b_mod_c (a, elgp - x - 1, elgp), elgp).Encode (m, 255);
if (!CryptoPP::SHA256().VerifyDigest (m + 1, m + 33, 222))
{
LogPrint ("ElGamal decrypt hash doesn't match");
return false;
}
memcpy (data, m + 33, 222);
return true;
}
inline void GenerateElGamalKeyPair (CryptoPP::RandomNumberGenerator& rnd, uint8_t * priv, uint8_t * pub)
{
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
rnd.GenerateBlock (priv, 256);
a_exp_b_mod_c (elgg, CryptoPP::Integer (priv, 256), elgp).Encode (pub, 256);
#else
CryptoPP::DH dh (elgp, elgg);
dh.GenerateKeyPair(rnd, priv, pub);
#endif
}
}
}
#endif

@ -2,12 +2,14 @@
#include "I2PEndian.h" #include "I2PEndian.h"
#include <map> #include <map>
#include <string> #include <string>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "RouterContext.h" #include "RouterContext.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Tunnel.h" #include "Tunnel.h"
#include "TunnelPool.h" #include "TunnelPool.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "Destination.h" #include "Log.h"
#include "Garlic.h" #include "Garlic.h"
namespace i2p namespace i2p
@ -17,10 +19,11 @@ namespace garlic
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner,
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet): std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
m_Owner (owner), m_Destination (destination), m_NumTags (numTags), m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend) m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ()))
{ {
// create new session tags and session key // create new session tags and session key
m_Rnd.GenerateBlock (m_SessionKey, 32); RAND_bytes (m_SessionKey, 32);
m_Encryption.SetKey (m_SessionKey); m_Encryption.SetKey (m_SessionKey);
} }
@ -46,7 +49,7 @@ namespace garlic
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch (); tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
for (int i = 0; i < m_NumTags; i++) for (int i = 0; i < m_NumTags; i++)
{ {
m_Rnd.GenerateBlock (tags->sessionTags[i], 32); RAND_bytes (tags->sessionTags[i], 32);
tags->sessionTags[i].creationTime = tags->tagsCreationTime; tags->sessionTags[i].creationTime = tags->tagsCreationTime;
} }
return tags; return tags;
@ -107,7 +110,7 @@ namespace garlic
return !m_SessionTags.empty () || m_UnconfirmedTagsMsgs.empty (); return !m_SessionTags.empty () || m_UnconfirmedTagsMsgs.empty ();
} }
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<I2NPMessage> msg) std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{ {
auto m = ToSharedI2NPMessage(NewI2NPMessage ()); auto m = ToSharedI2NPMessage(NewI2NPMessage ());
m->Align (12); // in order to get buf aligned to 16 (12 + 4) m->Align (12); // in order to get buf aligned to 16 (12 + 4)
@ -145,10 +148,10 @@ namespace garlic
// create ElGamal block // create ElGamal block
ElGamalBlock elGamal; ElGamalBlock elGamal;
memcpy (elGamal.sessionKey, m_SessionKey, 32); memcpy (elGamal.sessionKey, m_SessionKey, 32);
m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV RAND_bytes (elGamal.preIV, 32); // Pre-IV
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); SHA256(elGamal.preIV, 32, iv);
m_Destination->GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true); m_ElGamalEncryption->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
m_Encryption.SetIV (iv); m_Encryption.SetIV (iv);
buf += 514; buf += 514;
len += 514; len += 514;
@ -158,20 +161,20 @@ namespace garlic
// session tag // session tag
memcpy (buf, tag, 32); memcpy (buf, tag, 32);
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, tag, 32); SHA256(tag, 32, iv);
m_Encryption.SetIV (iv); m_Encryption.SetIV (iv);
buf += 32; buf += 32;
len += 32; len += 32;
} }
// AES block // AES block
len += CreateAESBlock (buf, msg.get ()); // TODO len += CreateAESBlock (buf, msg);
htobe32buf (m->GetPayload (), len); htobe32buf (m->GetPayload (), len);
m->len += len + 4; m->len += len + 4;
m->FillI2NPMessageHeader (eI2NPGarlic); m->FillI2NPMessageHeader (eI2NPGarlic);
return m; return m;
} }
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, const I2NPMessage * msg) size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg)
{ {
size_t blockSize = 0; size_t blockSize = 0;
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3); bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
@ -194,7 +197,7 @@ namespace garlic
blockSize++; blockSize++;
size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags); size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags);
htobe32buf (payloadSize, len); htobe32buf (payloadSize, len);
CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len); SHA256(buf + blockSize, len, payloadHash);
blockSize += len; blockSize += len;
size_t rem = blockSize % 16; size_t rem = blockSize % 16;
if (rem) if (rem)
@ -203,10 +206,11 @@ namespace garlic
return blockSize; return blockSize;
} }
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, const I2NPMessage * msg, UnconfirmedTags * newTags) size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
{ {
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
uint32_t msgID = m_Rnd.GenerateWord32 (); uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
size_t size = 0; size_t size = 0;
uint8_t * numCloves = payload + size; uint8_t * numCloves = payload + size;
*numCloves = 0; *numCloves = 0;
@ -243,8 +247,7 @@ namespace garlic
m_LeaseSetSubmissionTime = i2p::util::GetMillisecondsSinceEpoch (); m_LeaseSetSubmissionTime = i2p::util::GetMillisecondsSinceEpoch ();
// clove if our leaseSet must be attached // clove if our leaseSet must be attached
auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ()); auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ());
size += CreateGarlicClove (payload + size, leaseSet, false); size += CreateGarlicClove (payload + size, leaseSet, false);
DeleteI2NPMessage (leaseSet);
(*numCloves)++; (*numCloves)++;
} }
} }
@ -263,7 +266,7 @@ namespace garlic
return size; return size;
} }
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, const I2NPMessage * msg, bool isDestination) size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination)
{ {
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
size_t size = 0; size_t size = 0;
@ -282,7 +285,9 @@ namespace garlic
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength (); size += msg->GetLength ();
htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID uint32_t cloveID;
RAND_bytes ((uint8_t *)&cloveID, 4);
htobe32buf (buf + size, cloveID); // CloveID
size += 4; size += 4;
htobe64buf (buf + size, ts); // Expiration of clove htobe64buf (buf + size, ts); // Expiration of clove
size += 8; size += 8;
@ -312,8 +317,8 @@ namespace garlic
{ {
//encrypt //encrypt
uint8_t key[32], tag[32]; uint8_t key[32], tag[32];
m_Rnd.GenerateBlock (key, 32); // random session key RAND_bytes (key, 32); // random session key
m_Rnd.GenerateBlock (tag, 32); // random session tag RAND_bytes (tag, 32); // random session tag
m_Owner->SubmitSessionKey (key, tag); m_Owner->SubmitSessionKey (key, tag);
GarlicRoutingSession garlic (key, tag); GarlicRoutingSession garlic (key, tag);
msg = garlic.WrapSingleMessage (msg); msg = garlic.WrapSingleMessage (msg);
@ -322,7 +327,9 @@ namespace garlic
size += msg->GetLength (); size += msg->GetLength ();
// fill clove // fill clove
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID uint32_t cloveID;
RAND_bytes ((uint8_t *)&cloveID, 4);
htobe32buf (buf + size, cloveID); // CloveID
size += 4; size += 4;
htobe64buf (buf + size, ts); // Expiration of clove htobe64buf (buf + size, ts); // Expiration of clove
size += 8; size += 8;
@ -376,7 +383,7 @@ namespace garlic
if (length >= 32) if (length >= 32)
{ {
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
CryptoPP::SHA256().CalculateDigest(iv, buf, 32); SHA256(buf, 32, iv);
it->second->SetIV (iv); it->second->SetIV (iv);
it->second->Decrypt (buf + 32, length - 32, buf + 32); it->second->Decrypt (buf + 32, length - 32, buf + 32);
HandleAESBlock (buf + 32, length - 32, it->second, msg->from); HandleAESBlock (buf + 32, length - 32, it->second, msg->from);
@ -394,7 +401,7 @@ namespace garlic
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>(); auto decryption = std::make_shared<i2p::crypto::CBCDecryption>();
decryption->SetKey (elGamal.sessionKey); 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); SHA256(elGamal.preIV, 32, iv);
decryption->SetIV (iv); decryption->SetIV (iv);
decryption->Decrypt(buf + 514, length - 514, buf + 514); decryption->Decrypt(buf + 514, length - 514, buf + 514);
HandleAESBlock (buf + 514, length - 514, decryption, msg->from); HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
@ -458,7 +465,9 @@ namespace garlic
buf++; // flag buf++; // flag
// payload // payload
if (!CryptoPP::SHA256().VerifyDigest (payloadHash, buf, payloadSize)) // payload hash doesn't match uint8_t digest[32];
SHA256 (buf, payloadSize, digest);
if (memcmp (payloadHash, digest, 32)) // payload hash doesn't match
{ {
LogPrint ("Wrong payload hash"); LogPrint ("Wrong payload hash");
return; return;

@ -8,8 +8,7 @@
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <memory> #include <memory>
#include <cryptopp/osrng.h> #include "Crypto.h"
#include "aes.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "LeaseSet.h" #include "LeaseSet.h"
#include "Queue.h" #include "Queue.h"
@ -80,7 +79,7 @@ namespace garlic
int numTags, bool attachLeaseSet); int numTags, bool attachLeaseSet);
GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
~GarlicRoutingSession (); ~GarlicRoutingSession ();
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<I2NPMessage> msg); std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
void MessageConfirmed (uint32_t msgID); void MessageConfirmed (uint32_t msgID);
bool CleanupExpiredTags (); // returns true if something left bool CleanupExpiredTags (); // returns true if something left
@ -91,9 +90,9 @@ namespace garlic
private: private:
size_t CreateAESBlock (uint8_t * buf, const I2NPMessage * msg); size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
size_t CreateGarlicPayload (uint8_t * payload, const I2NPMessage * msg, UnconfirmedTags * newTags); size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags);
size_t CreateGarlicClove (uint8_t * buf, const I2NPMessage * msg, bool isDestination); size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination);
size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID); size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID);
void TagsConfirmed (uint32_t msgID); void TagsConfirmed (uint32_t msgID);
@ -113,7 +112,7 @@ namespace garlic
uint64_t m_LeaseSetSubmissionTime; // in milliseconds uint64_t m_LeaseSetSubmissionTime; // in milliseconds
i2p::crypto::CBCEncryption m_Encryption; i2p::crypto::CBCEncryption m_Encryption;
CryptoPP::AutoSeededRandomPool m_Rnd; std::unique_ptr<const i2p::crypto::ElGamalEncryption> m_ElGamalEncryption;
}; };
class GarlicDestination: public i2p::data::LocalDestination class GarlicDestination: public i2p::data::LocalDestination

@ -1,7 +1,7 @@
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include "base64.h" #include "Base.h"
#include "Log.h" #include "Log.h"
#include "Tunnel.h" #include "Tunnel.h"
#include "TransitTunnel.h" #include "TransitTunnel.h"
@ -466,7 +466,8 @@ namespace util
const char HTTP_COMMAND_TRANSIT_TUNNELS[] = "transit_tunnels"; const char HTTP_COMMAND_TRANSIT_TUNNELS[] = "transit_tunnels";
const char HTTP_COMMAND_TRANSPORTS[] = "transports"; const char HTTP_COMMAND_TRANSPORTS[] = "transports";
const char HTTP_COMMAND_START_ACCEPTING_TUNNELS[] = "start_accepting_tunnels"; const char HTTP_COMMAND_START_ACCEPTING_TUNNELS[] = "start_accepting_tunnels";
const char HTTP_COMMAND_STOP_ACCEPTING_TUNNELS[] = "stop_accepting_tunnels"; const char HTTP_COMMAND_STOP_ACCEPTING_TUNNELS[] = "stop_accepting_tunnels";
const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test";
const char HTTP_COMMAND_LOCAL_DESTINATIONS[] = "local_destinations"; const char HTTP_COMMAND_LOCAL_DESTINATIONS[] = "local_destinations";
const char HTTP_COMMAND_LOCAL_DESTINATION[] = "local_destination"; const char HTTP_COMMAND_LOCAL_DESTINATION[] = "local_destination";
const char HTTP_PARAM_BASE32_ADDRESS[] = "b32"; const char HTTP_PARAM_BASE32_ADDRESS[] = "b32";
@ -702,6 +703,7 @@ namespace util
s << "<br><b><a href=/?" << HTTP_COMMAND_STOP_ACCEPTING_TUNNELS << ">Stop accepting tunnels</a></b><br>"; s << "<br><b><a href=/?" << HTTP_COMMAND_STOP_ACCEPTING_TUNNELS << ">Stop accepting tunnels</a></b><br>";
else else
s << "<br><b><a href=/?" << HTTP_COMMAND_START_ACCEPTING_TUNNELS << ">Start accepting tunnels</a></b><br>"; s << "<br><b><a href=/?" << HTTP_COMMAND_START_ACCEPTING_TUNNELS << ">Start accepting tunnels</a></b><br>";
s << "<br><b><a href=/?" << HTTP_COMMAND_RUN_PEER_TEST << ">Run peer test</a></b><br>";
s << "<p><a href=\"zmw2cyw2vj7f6obx3msmdvdepdhnw2ctc4okza2zjxlukkdfckhq.b32.i2p\">Flibusta</a></p>"; s << "<p><a href=\"zmw2cyw2vj7f6obx3msmdvdepdhnw2ctc4okza2zjxlukkdfckhq.b32.i2p\">Flibusta</a></p>";
} }
@ -720,6 +722,8 @@ namespace util
StartAcceptingTunnels (s); StartAcceptingTunnels (s);
else if (cmd == HTTP_COMMAND_STOP_ACCEPTING_TUNNELS) else if (cmd == HTTP_COMMAND_STOP_ACCEPTING_TUNNELS)
StopAcceptingTunnels (s); StopAcceptingTunnels (s);
else if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
RunPeerTest (s);
else if (cmd == HTTP_COMMAND_LOCAL_DESTINATIONS) else if (cmd == HTTP_COMMAND_LOCAL_DESTINATIONS)
ShowLocalDestinations (s); ShowLocalDestinations (s);
else if (cmd == HTTP_COMMAND_LOCAL_DESTINATION) else if (cmd == HTTP_COMMAND_LOCAL_DESTINATION)
@ -751,11 +755,10 @@ namespace util
if (it.second && it.second->IsEstablished ()) if (it.second && it.second->IsEstablished ())
{ {
// incoming connection doesn't have remote RI // incoming connection doesn't have remote RI
auto outgoing = it.second->GetRemoteRouter (); if (it.second->IsOutgoing ()) s << "-->";
if (outgoing) s << "-->"; s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
s << it.second->GetRemoteIdentity ().GetIdentHash ().ToBase64 ().substr (0, 4) << ": "
<< it.second->GetSocket ().remote_endpoint().address ().to_string (); << it.second->GetSocket ().remote_endpoint().address ().to_string ();
if (!outgoing) s << "-->"; if (!it.second->IsOutgoing ()) s << "-->";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
s << "<br>"; s << "<br>";
} }
@ -769,11 +772,10 @@ namespace util
for (auto it: ssuServer->GetSessions ()) for (auto it: ssuServer->GetSessions ())
{ {
// incoming connections don't have remote router // incoming connections don't have remote router
auto outgoing = it.second->GetRemoteRouter ();
auto endpoint = it.second->GetRemoteEndpoint (); auto endpoint = it.second->GetRemoteEndpoint ();
if (outgoing) s << "-->"; if (it.second->IsOutgoing ()) s << "-->";
s << endpoint.address ().to_string () << ":" << endpoint.port (); s << endpoint.address ().to_string () << ":" << endpoint.port ();
if (!outgoing) s << "-->"; if (!it.second->IsOutgoing ()) s << "-->";
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ()) if (it.second->GetRelayTag ())
s << " [itag:" << it.second->GetRelayTag () << "]"; s << " [itag:" << it.second->GetRelayTag () << "]";
@ -789,7 +791,7 @@ namespace util
for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ()) for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ())
{ {
it->GetTunnelConfig ()->Print (s); it->Print (s);
auto state = it->GetState (); auto state = it->GetState ();
if (state == i2p::tunnel::eTunnelStateFailed) if (state == i2p::tunnel::eTunnelStateFailed)
s << " " << "Failed"; s << " " << "Failed";
@ -801,7 +803,7 @@ namespace util
for (auto it: i2p::tunnel::tunnels.GetInboundTunnels ()) for (auto it: i2p::tunnel::tunnels.GetInboundTunnels ())
{ {
it.second->GetTunnelConfig ()->Print (s); it.second->Print (s);
auto state = it.second->GetState (); auto state = it.second->GetState ();
if (state == i2p::tunnel::eTunnelStateFailed) if (state == i2p::tunnel::eTunnelStateFailed)
s << " " << "Failed"; s << " " << "Failed";
@ -844,7 +846,7 @@ namespace util
auto dest = i2p::client::context.FindLocalDestination (ident); auto dest = i2p::client::context.FindLocalDestination (ident);
if (dest) if (dest)
{ {
s << "<b>Base64:</b><br>" << dest->GetIdentity ().ToBase64 () << "<br><br>"; s << "<b>Base64:</b><br>" << dest->GetIdentity ()->ToBase64 () << "<br><br>";
s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>"; s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>";
auto pool = dest->GetTunnelPool (); auto pool = dest->GetTunnelPool ();
if (pool) if (pool)
@ -852,7 +854,7 @@ namespace util
s << "<b>Tunnels:</b><br>"; s << "<b>Tunnels:</b><br>";
for (auto it: pool->GetOutboundTunnels ()) for (auto it: pool->GetOutboundTunnels ())
{ {
it->GetTunnelConfig ()->Print (s); it->Print (s);
auto state = it->GetState (); auto state = it->GetState ();
if (state == i2p::tunnel::eTunnelStateFailed) if (state == i2p::tunnel::eTunnelStateFailed)
s << " " << "Failed"; s << " " << "Failed";
@ -862,7 +864,7 @@ namespace util
} }
for (auto it: pool->GetInboundTunnels ()) for (auto it: pool->GetInboundTunnels ())
{ {
it->GetTunnelConfig ()->Print (s); it->Print (s);
auto state = it->GetState (); auto state = it->GetState ();
if (state == i2p::tunnel::eTunnelStateFailed) if (state == i2p::tunnel::eTunnelStateFailed)
s << " " << "Failed"; s << " " << "Failed";
@ -948,6 +950,12 @@ namespace util
s << "Accepting tunnels stopped" << std::endl; s << "Accepting tunnels stopped" << std::endl;
} }
void HTTPConnection::RunPeerTest (std::stringstream& s)
{
i2p::transport::transports.PeerTest ();
s << "Peer test" << std::endl;
}
void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& uri) void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& uri)
{ {
std::string request = "GET " + uri + " HTTP/1.1\r\nHost:" + address + "\r\n"; std::string request = "GET " + uri + " HTTP/1.1\r\nHost:" + address + "\r\n";
@ -1044,7 +1052,7 @@ namespace util
HTTPServer::HTTPServer (int port): HTTPServer::HTTPServer (int port):
m_Thread (nullptr), m_Work (m_Service), m_Thread (nullptr), m_Work (m_Service),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4 (), port)),
m_NewSocket (nullptr) m_NewSocket (nullptr)
{ {

@ -73,6 +73,7 @@ namespace util
void ShowSAMSession (const std::string& id, std::stringstream& s); void ShowSAMSession (const std::string& id, std::stringstream& s);
void StartAcceptingTunnels (std::stringstream& s); void StartAcceptingTunnels (std::stringstream& s);
void StopAcceptingTunnels (std::stringstream& s); void StopAcceptingTunnels (std::stringstream& s);
void RunPeerTest (std::stringstream& s);
void FillContent (std::stringstream& s); void FillContent (std::stringstream& s);
std::string ExtractAddress (); std::string ExtractAddress ();
void ExtractParams (const std::string& str, std::map<std::string, std::string>& params); void ExtractParams (const std::string& str, std::map<std::string, std::string>& params);

@ -1,13 +1,15 @@
#include <string.h> #include <string.h>
#include <atomic> #include <atomic>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "Base.h"
#include "Log.h"
#include "Crypto.h"
#include "I2PEndian.h" #include "I2PEndian.h"
#include <cryptopp/gzip.h>
#include "ElGamal.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "NetDb.h" #include "NetDb.h"
#include "Tunnel.h" #include "Tunnel.h"
#include "base64.h"
#include "Transports.h" #include "Transports.h"
#include "Garlic.h" #include "Garlic.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
@ -44,10 +46,8 @@ namespace i2p
void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID) void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID)
{ {
SetTypeID (msgType); SetTypeID (msgType);
if (replyMsgID) // for tunnel creation if (!replyMsgID) RAND_bytes ((uint8_t *)&replyMsgID, 4);
SetMsgID (replyMsgID); SetMsgID (replyMsgID);
else
SetMsgID (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ());
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number
UpdateSize (); UpdateSize ();
UpdateChks (); UpdateChks ();
@ -55,7 +55,9 @@ namespace i2p
void I2NPMessage::RenewI2NPMessageHeader () void I2NPMessage::RenewI2NPMessageHeader ()
{ {
SetMsgID (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ()); uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
SetMsgID (msgID);
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000);
} }
@ -98,7 +100,8 @@ namespace i2p
} }
else // for SSU establishment else // for SSU establishment
{ {
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, i2p::context.GetRandomNumberGenerator ().GenerateWord32 ()); RAND_bytes ((uint8_t *)&msgID, 4);
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2 htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2
} }
m->len += DELIVERY_STATUS_SIZE; m->len += DELIVERY_STATUS_SIZE;
@ -106,10 +109,10 @@ namespace i2p
return ToSharedI2NPMessage (m); return ToSharedI2NPMessage (m);
} }
I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers) uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
{ {
I2NPMessage * m = excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage (); auto m = ToSharedI2NPMessage (excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage ());
uint8_t * buf = m->GetPayload (); uint8_t * buf = m->GetPayload ();
memcpy (buf, key, 32); // key memcpy (buf, key, 32); // key
buf += 32; buf += 32;
@ -151,12 +154,12 @@ namespace i2p
return m; return m;
} }
I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills, const std::set<i2p::data::IdentHash>& excludedFloodfills,
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag) const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag)
{ {
int cnt = excludedFloodfills.size (); int cnt = excludedFloodfills.size ();
I2NPMessage * m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage (); auto m = ToSharedI2NPMessage (cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage ());
uint8_t * buf = m->GetPayload (); uint8_t * buf = m->GetPayload ();
memcpy (buf, dest, 32); // key memcpy (buf, dest, 32); // key
buf += 32; buf += 32;
@ -188,10 +191,10 @@ namespace i2p
return m; return m;
} }
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
std::vector<i2p::data::IdentHash> routers) std::vector<i2p::data::IdentHash> routers)
{ {
I2NPMessage * m = NewI2NPShortMessage (); auto m = ToSharedI2NPMessage (NewI2NPShortMessage ());
uint8_t * buf = m->GetPayload (); uint8_t * buf = m->GetPayload ();
size_t len = 0; size_t len = 0;
memcpy (buf, ident, 32); memcpy (buf, ident, 32);
@ -210,12 +213,12 @@ namespace i2p
return m; return m;
} }
I2NPMessage * CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router, uint32_t replyToken) std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router, uint32_t replyToken)
{ {
if (!router) // we send own RouterInfo if (!router) // we send own RouterInfo
router = context.GetSharedRouterInfo (); router = context.GetSharedRouterInfo ();
I2NPMessage * m = NewI2NPShortMessage (); auto m = ToSharedI2NPMessage (NewI2NPShortMessage ());
uint8_t * payload = m->GetPayload (); uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32); memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32);
@ -230,33 +233,27 @@ namespace i2p
buf += 32; buf += 32;
} }
CryptoPP::Gzip compressor; uint8_t * sizePtr = buf;
compressor.Put (router->GetBuffer (), router->GetBufferLen ());
compressor.MessageEnd();
auto size = compressor.MaxRetrievable ();
htobe16buf (buf, size); // size
buf += 2; buf += 2;
m->len += (buf - payload); // payload size m->len += (buf - payload); // payload size
if (m->len + size > m->maxLen) i2p::data::GzipDeflator deflator;
size_t size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len);
if (size)
{ {
LogPrint (eLogInfo, "DatabaseStore message size is not enough for ", m->len + size); htobe16buf (sizePtr, size); // size
auto newMsg = NewI2NPMessage (); m->len += size;
*newMsg = *m;
DeleteI2NPMessage (m);
m = newMsg;
buf = m->buf + m->len;
} }
compressor.Get (buf, size); else
m->len += size; m = nullptr;
m->FillI2NPMessageHeader (eI2NPDatabaseStore); if (m)
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
return m; return m;
} }
I2NPMessage * CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken) std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken)
{ {
if (!leaseSet) return nullptr; if (!leaseSet) return nullptr;
I2NPMessage * m = NewI2NPShortMessage (); auto m = ToSharedI2NPMessage (NewI2NPShortMessage ());
uint8_t * payload = m->GetPayload (); uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32); memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
@ -313,8 +310,8 @@ namespace i2p
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30) record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30)
//TODO: fill filler //TODO: fill filler
CryptoPP::SHA256().CalculateDigest(record + BUILD_RESPONSE_RECORD_HASH_OFFSET, SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret
record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1); // + 1 byte of ret record + BUILD_RESPONSE_RECORD_HASH_OFFSET);
// encrypt reply // encrypt reply
i2p::crypto::CBCEncryption encryption; i2p::crypto::CBCEncryption encryption;
for (int j = 0; j < num; j++) for (int j = 0; j < num; j++)

@ -5,7 +5,7 @@
#include <string.h> #include <string.h>
#include <set> #include <set>
#include <memory> #include <memory>
#include <cryptopp/sha.h> #include <openssl/sha.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
@ -132,7 +132,7 @@ namespace tunnel
void UpdateChks () void UpdateChks ()
{ {
uint8_t hash[32]; uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest(hash, GetPayload (), GetPayloadLength ()); SHA256(GetPayload (), GetPayloadLength (), hash);
GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0]; GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
} }
@ -206,15 +206,15 @@ namespace tunnel
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr); std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID); std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID);
I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr); uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills, const std::set<i2p::data::IdentHash>& excludedFloodfills,
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag); const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers); std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
I2NPMessage * CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0); std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);
I2NPMessage * CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken = 0); std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken = 0);
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText); bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);

@ -83,11 +83,11 @@ namespace client
public: public:
TCPIPAcceptor (int port, std::shared_ptr<ClientDestination> localDestination = nullptr) : TCPIPAcceptor (int port, std::shared_ptr<ClientDestination> localDestination = nullptr) :
I2PService(localDestination), I2PService(localDestination),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4 (), port)),
m_Timer (GetService ()) {} m_Timer (GetService ()) {}
TCPIPAcceptor (int port, i2p::data::SigningKeyType kt) : TCPIPAcceptor (int port, i2p::data::SigningKeyType kt) :
I2PService(kt), I2PService(kt),
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4 (), port)),
m_Timer (GetService ()) {} m_Timer (GetService ()) {}
virtual ~TCPIPAcceptor () { TCPIPAcceptor::Stop(); } virtual ~TCPIPAcceptor () { TCPIPAcceptor::Stop(); }
//If you override this make sure you call it from the children //If you override this make sure you call it from the children

@ -1,5 +1,5 @@
#include <cassert> #include <cassert>
#include "base64.h" #include "Base.h"
#include "Log.h" #include "Log.h"
#include "Destination.h" #include "Destination.h"
#include "ClientContext.h" #include "ClientContext.h"
@ -153,7 +153,7 @@ namespace client
else else
{ {
// send destination first like received from I2P // send destination first like received from I2P
std::string dest = m_Stream->GetRemoteIdentity ().ToBase64 (); std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 ();
dest += "\n"; dest += "\n";
memcpy (m_StreamBuffer, dest.c_str (), dest.size ()); memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
HandleStreamReceive (boost::system::error_code (), dest.size ()); HandleStreamReceive (boost::system::error_code (), dest.size ());
@ -369,9 +369,9 @@ namespace client
{ {
if (m_IsAccessList) if (m_IsAccessList)
{ {
if (!m_AccessList.count (stream->GetRemoteIdentity ().GetIdentHash ())) if (!m_AccessList.count (stream->GetRemoteIdentity ()->GetIdentHash ()))
{ {
LogPrint (eLogWarning, "Address ", stream->GetRemoteIdentity ().GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped"); LogPrint (eLogWarning, "Address ", stream->GetRemoteIdentity ()->GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped");
stream->Close (); stream->Close ();
return; return;
} }

@ -1,14 +1,12 @@
#include <time.h> #include <time.h>
#include <stdio.h> #include <stdio.h>
#include <cryptopp/sha.h> #include <openssl/sha.h>
#include <cryptopp/osrng.h> #include <openssl/dh.h>
#include <cryptopp/dsa.h> #include <openssl/rand.h>
#include "base64.h" #include "Crypto.h"
#include "CryptoConst.h"
#include "ElGamal.h"
#include "RouterContext.h"
#include "Identity.h"
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Log.h"
#include "Identity.h"
namespace i2p namespace i2p
{ {
@ -31,12 +29,12 @@ namespace data
IdentHash Identity::Hash () const IdentHash Identity::Hash () const
{ {
IdentHash hash; IdentHash hash;
CryptoPP::SHA256().CalculateDigest(hash, publicKey, DEFAULT_IDENTITY_SIZE); SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash);
return hash; return hash;
} }
IdentityEx::IdentityEx (): IdentityEx::IdentityEx ():
m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr) m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{ {
} }
@ -52,14 +50,14 @@ namespace data
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
{ {
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64 size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding); RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH); memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH);
break; break;
} }
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
{ {
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96 size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding); RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH); memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH);
break; break;
} }
@ -98,7 +96,7 @@ namespace data
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
{ {
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32 size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding); RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH); memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
break; break;
} }
@ -121,7 +119,7 @@ namespace data
// calculate ident hash // calculate ident hash
uint8_t * buf = new uint8_t[GetFullLen ()]; uint8_t * buf = new uint8_t[GetFullLen ()];
ToBuffer (buf, GetFullLen ()); ToBuffer (buf, GetFullLen ());
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ()); SHA256(buf, GetFullLen (), m_IdentHash);
delete[] buf; delete[] buf;
} }
else // DSA-SHA1 else // DSA-SHA1
@ -136,20 +134,25 @@ namespace data
} }
IdentityEx::IdentityEx (const uint8_t * buf, size_t len): IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr) m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{ {
FromBuffer (buf, len); FromBuffer (buf, len);
} }
IdentityEx::IdentityEx (const IdentityEx& other): IdentityEx::IdentityEx (const IdentityEx& other):
m_Verifier (nullptr), m_ExtendedBuffer (nullptr) m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{ {
*this = other; *this = other;
} }
IdentityEx::IdentityEx (const Identity& standard):
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
{
*this = standard;
}
IdentityEx::~IdentityEx () IdentityEx::~IdentityEx ()
{ {
delete m_Verifier;
delete[] m_ExtendedBuffer; delete[] m_ExtendedBuffer;
} }
@ -168,7 +171,6 @@ namespace data
else else
m_ExtendedBuffer = nullptr; m_ExtendedBuffer = nullptr;
delete m_Verifier;
m_Verifier = nullptr; m_Verifier = nullptr;
return *this; return *this;
@ -183,7 +185,6 @@ namespace data
m_ExtendedBuffer = nullptr; m_ExtendedBuffer = nullptr;
m_ExtendedLen = 0; m_ExtendedLen = 0;
delete m_Verifier;
m_Verifier = nullptr; m_Verifier = nullptr;
return *this; return *this;
@ -218,9 +219,8 @@ namespace data
m_ExtendedLen = 0; m_ExtendedLen = 0;
m_ExtendedBuffer = nullptr; m_ExtendedBuffer = nullptr;
} }
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ()); SHA256(buf, GetFullLen (), m_IdentHash);
delete m_Verifier;
m_Verifier = nullptr; m_Verifier = nullptr;
return GetFullLen (); return GetFullLen ();
@ -302,18 +302,18 @@ namespace data
switch (keyType) switch (keyType)
{ {
case SIGNING_KEY_TYPE_DSA_SHA1: case SIGNING_KEY_TYPE_DSA_SHA1:
m_Verifier = new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey); m_Verifier.reset (new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey));
break; break;
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
{ {
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64 size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
m_Verifier = new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding); m_Verifier.reset (new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding));
break; break;
} }
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
{ {
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96 size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
m_Verifier = new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding); m_Verifier.reset (new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding));
break; break;
} }
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
@ -322,7 +322,7 @@ namespace data
memcpy (signingKey, m_StandardIdentity.signingKey, 128); memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128 size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier = new i2p::crypto::ECDSAP521Verifier (signingKey); m_Verifier.reset (new i2p::crypto::ECDSAP521Verifier (signingKey));
break; break;
} }
case SIGNING_KEY_TYPE_RSA_SHA256_2048: case SIGNING_KEY_TYPE_RSA_SHA256_2048:
@ -331,7 +331,7 @@ namespace data
memcpy (signingKey, m_StandardIdentity.signingKey, 128); memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128 size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier = new i2p::crypto:: RSASHA2562048Verifier (signingKey); m_Verifier.reset (new i2p::crypto:: RSASHA2562048Verifier (signingKey));
break; break;
} }
case SIGNING_KEY_TYPE_RSA_SHA384_3072: case SIGNING_KEY_TYPE_RSA_SHA384_3072:
@ -340,7 +340,7 @@ namespace data
memcpy (signingKey, m_StandardIdentity.signingKey, 128); memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128 size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier = new i2p::crypto:: RSASHA3843072Verifier (signingKey); m_Verifier.reset (new i2p::crypto:: RSASHA3843072Verifier (signingKey));
break; break;
} }
case SIGNING_KEY_TYPE_RSA_SHA512_4096: case SIGNING_KEY_TYPE_RSA_SHA512_4096:
@ -349,13 +349,13 @@ namespace data
memcpy (signingKey, m_StandardIdentity.signingKey, 128); memcpy (signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128 size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
m_Verifier = new i2p::crypto:: RSASHA5124096Verifier (signingKey); m_Verifier.reset (new i2p::crypto:: RSASHA5124096Verifier (signingKey));
break; break;
} }
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
{ {
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32 size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
m_Verifier = new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding); m_Verifier.reset (new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding));
break; break;
} }
default: default:
@ -363,19 +363,16 @@ namespace data
} }
} }
void IdentityEx::DropVerifier () void IdentityEx::DropVerifier () const
{ {
auto verifier = m_Verifier; m_Verifier = nullptr;
m_Verifier = nullptr; // TODO: make this atomic
delete verifier;
} }
PrivateKeys& PrivateKeys::operator=(const Keys& keys) PrivateKeys& PrivateKeys::operator=(const Keys& keys)
{ {
m_Public = Identity (keys); m_Public = std::make_shared<IdentityEx>(Identity (keys));
memcpy (m_PrivateKey, keys.privateKey, 256); // 256 memcpy (m_PrivateKey, keys.privateKey, 256); // 256
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public.GetSigningPrivateKeyLen ()); memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen ());
delete m_Signer;
m_Signer = nullptr; m_Signer = nullptr;
CreateSigner (); CreateSigner ();
return *this; return *this;
@ -383,10 +380,9 @@ namespace data
PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other) PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other)
{ {
m_Public = other.m_Public; m_Public = std::make_shared<IdentityEx>(*other.m_Public);
memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256 memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public.GetSigningPrivateKeyLen ()); memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public->GetSigningPrivateKeyLen ());
delete m_Signer;
m_Signer = nullptr; m_Signer = nullptr;
CreateSigner (); CreateSigner ();
return *this; return *this;
@ -394,13 +390,13 @@ namespace data
size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len) size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len)
{ {
size_t ret = m_Public.FromBuffer (buf, len); m_Public = std::make_shared<IdentityEx>(buf, len);
size_t ret = m_Public->GetFullLen ();
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256 memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
ret += 256; ret += 256;
size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen (); size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize); memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
ret += signingPrivateKeySize; ret += signingPrivateKeySize;
delete m_Signer;
m_Signer = nullptr; m_Signer = nullptr;
CreateSigner (); CreateSigner ();
return ret; return ret;
@ -408,10 +404,10 @@ namespace data
size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const
{ {
size_t ret = m_Public.ToBuffer (buf, len); size_t ret = m_Public->ToBuffer (buf, len);
memcpy (buf + ret, m_PrivateKey, 256); // private key always 256 memcpy (buf + ret, m_PrivateKey, 256); // private key always 256
ret += 256; ret += 256;
size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen (); size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize); memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
ret += signingPrivateKeySize; ret += signingPrivateKeySize;
return ret; return ret;
@ -442,39 +438,39 @@ namespace data
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{ {
if (m_Signer) if (m_Signer)
m_Signer->Sign (i2p::context.GetRandomNumberGenerator (), buf, len, signature); m_Signer->Sign (buf, len, signature);
} }
void PrivateKeys::CreateSigner () void PrivateKeys::CreateSigner ()
{ {
switch (m_Public.GetSigningKeyType ()) switch (m_Public->GetSigningKeyType ())
{ {
case SIGNING_KEY_TYPE_DSA_SHA1: case SIGNING_KEY_TYPE_DSA_SHA1:
m_Signer = new i2p::crypto::DSASigner (m_SigningPrivateKey); m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey));
break; break;
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
m_Signer = new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey); m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey));
break; break;
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
m_Signer = new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey); m_Signer.reset (new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey));
break; break;
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
m_Signer = new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey); m_Signer.reset (new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey));
break; break;
case SIGNING_KEY_TYPE_RSA_SHA256_2048: case SIGNING_KEY_TYPE_RSA_SHA256_2048:
m_Signer = new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey); m_Signer.reset (new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey));
break; break;
case SIGNING_KEY_TYPE_RSA_SHA384_3072: case SIGNING_KEY_TYPE_RSA_SHA384_3072:
m_Signer = new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey); m_Signer.reset (new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey));
break; break;
case SIGNING_KEY_TYPE_RSA_SHA512_4096: case SIGNING_KEY_TYPE_RSA_SHA512_4096:
m_Signer = new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey); m_Signer.reset (new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey));
break; break;
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
m_Signer = new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey); m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey));
break; break;
default: default:
LogPrint ("Signing key type ", (int)m_Public.GetSigningKeyType (), " is not supported"); LogPrint ("Signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported");
} }
} }
@ -483,39 +479,40 @@ namespace data
if (type != SIGNING_KEY_TYPE_DSA_SHA1) if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{ {
PrivateKeys keys; PrivateKeys keys;
auto& rnd = i2p::context.GetRandomNumberGenerator ();
// signature // signature
uint8_t signingPublicKey[512]; // signing public key is 512 bytes max uint8_t signingPublicKey[512]; // signing public key is 512 bytes max
switch (type) switch (type)
{ {
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
i2p::crypto::CreateECDSAP256RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey); i2p::crypto::CreateECDSAP256RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break; break;
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
i2p::crypto::CreateECDSAP384RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey); i2p::crypto::CreateECDSAP384RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break; break;
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
i2p::crypto::CreateECDSAP521RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey); i2p::crypto::CreateECDSAP521RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break; break;
case SIGNING_KEY_TYPE_RSA_SHA256_2048: case SIGNING_KEY_TYPE_RSA_SHA256_2048:
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey); i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
break; break;
case SIGNING_KEY_TYPE_RSA_SHA384_3072: case SIGNING_KEY_TYPE_RSA_SHA384_3072:
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey); i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
break; break;
case SIGNING_KEY_TYPE_RSA_SHA512_4096: case SIGNING_KEY_TYPE_RSA_SHA512_4096:
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey); i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
break; break;
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
i2p::crypto::CreateEDDSA25519RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
break;
default: default:
LogPrint ("Signing key type ", (int)type, " is not supported. Create DSA-SHA1"); LogPrint ("Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1 return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
} }
// encryption // encryption
uint8_t publicKey[256]; uint8_t publicKey[256];
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); i2p::crypto::GenerateElGamalKeyPair (keys.m_PrivateKey, publicKey);
dh.GenerateKeyPair(rnd, keys.m_PrivateKey, publicKey);
// identity // identity
keys.m_Public = IdentityEx (publicKey, signingPublicKey, type); keys.m_Public = std::make_shared<IdentityEx> (publicKey, signingPublicKey, type);
keys.CreateSigner (); keys.CreateSigner ();
return keys; return keys;
@ -526,11 +523,10 @@ namespace data
Keys CreateRandomKeys () Keys CreateRandomKeys ()
{ {
Keys keys; Keys keys;
auto& rnd = i2p::context.GetRandomNumberGenerator ();
// encryption // encryption
i2p::crypto::GenerateElGamalKeyPair(rnd, keys.privateKey, keys.publicKey); i2p::crypto::GenerateElGamalKeyPair(keys.privateKey, keys.publicKey);
// signing // signing
i2p::crypto::CreateDSARandomKeys (rnd, keys.signingPrivateKey, keys.signingKey); i2p::crypto::CreateDSARandomKeys (keys.signingPrivateKey, keys.signingKey);
return keys; return keys;
} }
@ -548,7 +544,7 @@ namespace data
sprintf((char *)(buf + 32), "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); sprintf((char *)(buf + 32), "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
#endif #endif
IdentHash key; IdentHash key;
CryptoPP::SHA256().CalculateDigest((uint8_t *)key, buf, 40); SHA256(buf, 40, key);
return key; return key;
} }

@ -5,84 +5,18 @@
#include <string.h> #include <string.h>
#include <string> #include <string>
#include <memory> #include <memory>
#include "base64.h" #include "Base.h"
#include "ElGamal.h"
#include "Signature.h" #include "Signature.h"
namespace i2p namespace i2p
{ {
namespace data namespace data
{ {
template<int sz>
class Tag
{
public:
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); };
Tag (const Tag<sz>& ) = default;
#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it
Tag (Tag<sz>&& ) = default;
#endif
Tag () = default;
Tag<sz>& operator= (const Tag<sz>& ) = default;
#ifndef _WIN32
Tag<sz>& operator= (Tag<sz>&& ) = default;
#endif
uint8_t * operator()() { return m_Buf; };
const uint8_t * operator()() const { return m_Buf; };
operator uint8_t * () { return m_Buf; };
operator const uint8_t * () const { return m_Buf; };
const uint64_t * GetLL () const { return ll; };
bool operator== (const Tag<sz>& other) const { return !memcmp (m_Buf, other.m_Buf, sz); };
bool operator< (const Tag<sz>& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; };
bool IsZero () const
{
for (int i = 0; i < sz/8; i++)
if (ll[i]) return false;
return true;
}
std::string ToBase64 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
std::string ToBase32 () const
{
char str[sz*2];
int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
str[l] = 0;
return std::string (str);
}
void FromBase32 (const std::string& s)
{
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
void FromBase64 (const std::string& s)
{
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
private:
union // 8 bytes alignment
{
uint8_t m_Buf[sz];
uint64_t ll[sz/8];
};
};
typedef Tag<32> IdentHash; typedef Tag<32> IdentHash;
inline std::string GetIdentHashAbbreviation (const IdentHash& ident)
{
return ident.ToBase64 ().substr (0, 4);
}
#pragma pack(1) #pragma pack(1)
struct Keys struct Keys
@ -142,6 +76,7 @@ namespace data
SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1); SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1);
IdentityEx (const uint8_t * buf, size_t len); IdentityEx (const uint8_t * buf, size_t len);
IdentityEx (const IdentityEx& other); IdentityEx (const IdentityEx& other);
IdentityEx (const Identity& standard);
~IdentityEx (); ~IdentityEx ();
IdentityEx& operator=(const IdentityEx& other); IdentityEx& operator=(const IdentityEx& other);
IdentityEx& operator=(const Identity& standard); IdentityEx& operator=(const Identity& standard);
@ -152,6 +87,7 @@ namespace data
std::string ToBase64 () const; std::string ToBase64 () const;
const Identity& GetStandardIdentity () const { return m_StandardIdentity; }; const Identity& GetStandardIdentity () const { return m_StandardIdentity; };
const IdentHash& GetIdentHash () const { return m_IdentHash; }; const IdentHash& GetIdentHash () const { return m_IdentHash; };
const uint8_t * GetEncryptionPublicKey () const { return m_StandardIdentity.publicKey; };
size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; }; size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; };
size_t GetSigningPublicKeyLen () const; size_t GetSigningPublicKeyLen () const;
size_t GetSigningPrivateKeyLen () const; size_t GetSigningPrivateKeyLen () const;
@ -159,7 +95,7 @@ namespace data
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
SigningKeyType GetSigningKeyType () const; SigningKeyType GetSigningKeyType () const;
CryptoKeyType GetCryptoKeyType () const; CryptoKeyType GetCryptoKeyType () const;
void DropVerifier (); // to save memory void DropVerifier () const; // to save memory
private: private:
@ -169,7 +105,7 @@ namespace data
Identity m_StandardIdentity; Identity m_StandardIdentity;
IdentHash m_IdentHash; IdentHash m_IdentHash;
mutable i2p::crypto::Verifier * m_Verifier; mutable std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
size_t m_ExtendedLen; size_t m_ExtendedLen;
uint8_t * m_ExtendedBuffer; uint8_t * m_ExtendedBuffer;
}; };
@ -178,19 +114,19 @@ namespace data
{ {
public: public:
PrivateKeys (): m_Signer (nullptr) {}; PrivateKeys () = default;
PrivateKeys (const PrivateKeys& other): m_Signer (nullptr) { *this = other; }; PrivateKeys (const PrivateKeys& other) { *this = other; };
PrivateKeys (const Keys& keys): m_Signer (nullptr) { *this = keys; }; PrivateKeys (const Keys& keys) { *this = keys; };
PrivateKeys& operator=(const Keys& keys); PrivateKeys& operator=(const Keys& keys);
PrivateKeys& operator=(const PrivateKeys& other); PrivateKeys& operator=(const PrivateKeys& other);
~PrivateKeys () { delete m_Signer; }; ~PrivateKeys () = default;
const IdentityEx& GetPublic () const { return m_Public; }; std::shared_ptr<const IdentityEx> GetPublic () const { return m_Public; };
const uint8_t * GetPrivateKey () const { return m_PrivateKey; }; const uint8_t * GetPrivateKey () const { return m_PrivateKey; };
const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; }; const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const; void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
size_t GetFullLen () const { return m_Public.GetFullLen () + 256 + m_Public.GetSigningPrivateKeyLen (); }; size_t GetFullLen () const { return m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen (); };
size_t FromBuffer (const uint8_t * buf, size_t len); size_t FromBuffer (const uint8_t * buf, size_t len);
size_t ToBuffer (uint8_t * buf, size_t len) const; size_t ToBuffer (uint8_t * buf, size_t len) const;
@ -205,10 +141,10 @@ namespace data
private: private:
IdentityEx m_Public; std::shared_ptr<IdentityEx> m_Public;
uint8_t m_PrivateKey[256]; uint8_t m_PrivateKey[256];
uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes
i2p::crypto::Signer * m_Signer; std::unique_ptr<i2p::crypto::Signer> m_Signer;
}; };
// kademlia // kademlia
@ -239,17 +175,6 @@ namespace data
virtual const IdentHash& GetIdentHash () const = 0; virtual const IdentHash& GetIdentHash () const = 0;
virtual const uint8_t * GetEncryptionPublicKey () const = 0; virtual const uint8_t * GetEncryptionPublicKey () const = 0;
virtual bool IsDestination () const = 0; // for garlic virtual bool IsDestination () const = 0; // for garlic
std::unique_ptr<const i2p::crypto::ElGamalEncryption>& GetElGamalEncryption () const
{
if (!m_ElGamalEncryption)
m_ElGamalEncryption.reset (new i2p::crypto::ElGamalEncryption (GetEncryptionPublicKey ()));
return m_ElGamalEncryption;
}
private:
mutable std::unique_ptr<const i2p::crypto::ElGamalEncryption> m_ElGamalEncryption; // use lazy initialization
}; };
class LocalDestination class LocalDestination
@ -261,8 +186,8 @@ namespace data
virtual const uint8_t * GetEncryptionPrivateKey () const = 0; virtual const uint8_t * GetEncryptionPrivateKey () const = 0;
virtual const uint8_t * GetEncryptionPublicKey () const = 0; virtual const uint8_t * GetEncryptionPublicKey () const = 0;
const IdentityEx& GetIdentity () const { return GetPrivateKeys ().GetPublic (); }; std::shared_ptr<const IdentityEx> GetIdentity () const { return GetPrivateKeys ().GetPublic (); };
const IdentHash& GetIdentHash () const { return GetIdentity ().GetIdentHash (); }; const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{ {
GetPrivateKeys ().Sign (buf, len, signature); GetPrivateKeys ().Sign (buf, len, signature);

@ -1,8 +1,6 @@
#include <string.h> #include <string.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include <cryptopp/dsa.h> #include "Crypto.h"
#include <cryptopp/osrng.h>
#include "CryptoConst.h"
#include "Log.h" #include "Log.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "NetDb.h" #include "NetDb.h"
@ -37,17 +35,16 @@ namespace data
return; return;
} }
m_Buffer = new uint8_t[MAX_LS_BUFFER_SIZE]; m_Buffer = new uint8_t[MAX_LS_BUFFER_SIZE];
m_BufferLen = localDestination->GetIdentity ().ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE); m_BufferLen = localDestination->GetIdentity ()->ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE);
memcpy (m_Buffer + m_BufferLen, localDestination->GetEncryptionPublicKey (), 256); memcpy (m_Buffer + m_BufferLen, localDestination->GetEncryptionPublicKey (), 256);
m_BufferLen += 256; m_BufferLen += 256;
auto signingKeyLen = localDestination->GetIdentity ().GetSigningPublicKeyLen (); auto signingKeyLen = localDestination->GetIdentity ()->GetSigningPublicKeyLen ();
memset (m_Buffer + m_BufferLen, 0, signingKeyLen); memset (m_Buffer + m_BufferLen, 0, signingKeyLen);
m_BufferLen += signingKeyLen; m_BufferLen += signingKeyLen;
auto tunnels = pool.GetInboundTunnels (5); // 5 tunnels maximum auto tunnels = pool.GetInboundTunnels (5); // 5 tunnels maximum
m_Buffer[m_BufferLen] = tunnels.size (); // num leases m_Buffer[m_BufferLen] = tunnels.size (); // num leases
m_BufferLen++; m_BufferLen++;
// leases // leases
CryptoPP::AutoSeededRandomPool rnd;
for (auto it: tunnels) for (auto it: tunnels)
{ {
memcpy (m_Buffer + m_BufferLen, it->GetNextIdentHash (), 32); memcpy (m_Buffer + m_BufferLen, it->GetNextIdentHash (), 32);
@ -56,13 +53,13 @@ namespace data
m_BufferLen += 4; // tunnel id m_BufferLen += 4; // tunnel id
uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
ts *= 1000; // in milliseconds ts *= 1000; // in milliseconds
ts += rnd.GenerateWord32 (0, 5); // + random milliseconds ts += rand () % 6; // + random milliseconds 0-5
htobe64buf (m_Buffer + m_BufferLen, ts); htobe64buf (m_Buffer + m_BufferLen, ts);
m_BufferLen += 8; // end date m_BufferLen += 8; // end date
} }
// signature // signature
localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen); localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen);
m_BufferLen += localDestination->GetIdentity ().GetSignatureLen (); m_BufferLen += localDestination->GetIdentity ()->GetSignatureLen ();
LogPrint ("Local LeaseSet of ", tunnels.size (), " leases created"); LogPrint ("Local LeaseSet of ", tunnels.size (), " leases created");
ReadFromBuffer (); ReadFromBuffer ();
@ -79,15 +76,17 @@ namespace data
} }
memcpy (m_Buffer, buf, len); memcpy (m_Buffer, buf, len);
m_BufferLen = len; m_BufferLen = len;
ReadFromBuffer (); ReadFromBuffer (false);
} }
void LeaseSet::ReadFromBuffer () void LeaseSet::ReadFromBuffer (bool readIdentity)
{ {
size_t size = m_Identity.FromBuffer (m_Buffer, m_BufferLen); if (readIdentity || !m_Identity)
m_Identity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
size_t size = m_Identity->GetFullLen ();
memcpy (m_EncryptionKey, m_Buffer + size, 256); memcpy (m_EncryptionKey, m_Buffer + size, 256);
size += 256; // encryption key size += 256; // encryption key
size += m_Identity.GetSigningPublicKeyLen (); // unused signing key size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
uint8_t num = m_Buffer[size]; uint8_t num = m_Buffer[size];
size++; // num size++; // num
LogPrint ("LeaseSet num=", (int)num); LogPrint ("LeaseSet num=", (int)num);
@ -116,7 +115,7 @@ namespace data
} }
// verify // verify
if (!m_Identity.Verify (m_Buffer, leases - m_Buffer, leases)) if (!m_Identity->Verify (m_Buffer, leases - m_Buffer, leases))
{ {
LogPrint (eLogWarning, "LeaseSet verification failed"); LogPrint (eLogWarning, "LeaseSet verification failed");
m_IsValid = false; m_IsValid = false;

@ -40,14 +40,14 @@ namespace data
LeaseSet (const i2p::tunnel::TunnelPool& pool); LeaseSet (const i2p::tunnel::TunnelPool& pool);
~LeaseSet () { delete[] m_Buffer; }; ~LeaseSet () { delete[] m_Buffer; };
void Update (const uint8_t * buf, size_t len); void Update (const uint8_t * buf, size_t len);
const IdentityEx& GetIdentity () const { return m_Identity; }; std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
const uint8_t * GetBuffer () const { return m_Buffer; }; const uint8_t * GetBuffer () const { return m_Buffer; };
size_t GetBufferLen () const { return m_BufferLen; }; size_t GetBufferLen () const { return m_BufferLen; };
bool IsValid () const { return m_IsValid; }; bool IsValid () const { return m_IsValid; };
// implements RoutingDestination // implements RoutingDestination
const IdentHash& GetIdentHash () const { return m_Identity.GetIdentHash (); }; const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
const std::vector<Lease>& GetLeases () const { return m_Leases; }; const std::vector<Lease>& GetLeases () const { return m_Leases; };
const std::vector<Lease> GetNonExpiredLeases (bool withThreshold = true) const; const std::vector<Lease> GetNonExpiredLeases (bool withThreshold = true) const;
bool HasExpiredLeases () const; bool HasExpiredLeases () const;
@ -57,13 +57,13 @@ namespace data
private: private:
void ReadFromBuffer (); void ReadFromBuffer (bool readIdentity = true);
private: private:
bool m_IsValid; bool m_IsValid;
std::vector<Lease> m_Leases; std::vector<Lease> m_Leases;
IdentityEx m_Identity; std::shared_ptr<const IdentityEx> m_Identity;
uint8_t m_EncryptionKey[256]; uint8_t m_EncryptionKey[256];
uint8_t * m_Buffer; uint8_t * m_Buffer;
size_t m_BufferLen; size_t m_BufferLen;

@ -1,5 +1,8 @@
UNAME := $(shell uname -s) UNAME := $(shell uname -s)
SHLIB := libi2pd.so SHLIB := libi2pd.so
ARLIB := libi2pd.a
SHLIB_CLIENT := libi2pdclient.so
ARLIB_CLIENT := libi2pdclient.a
I2PD := i2p I2PD := i2p
GREP := fgrep GREP := fgrep
DEPS := obj/make.dep DEPS := obj/make.dep
@ -22,12 +25,12 @@ else # win32
DAEMON_SRC += DaemonWin32.cpp DAEMON_SRC += DaemonWin32.cpp
endif endif
all: mk_build_dir $(SHLIB) $(I2PD) all: mk_build_dir $(SHLIB) $(SHLIB_CLIENT) $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
mk_build_dir: mk_build_dir:
mkdir -p obj mkdir -p obj
api: $(SHLIB) api: $(SHLIB) $(ARLIB)
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time ## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
## **without** overwriting the CXXFLAGS which we need in order to build. ## **without** overwriting the CXXFLAGS which we need in order to build.
@ -48,7 +51,7 @@ obj/%.o : %.cpp
# '-' is 'ignore if missing' on first run # '-' is 'ignore if missing' on first run
-include $(DEPS) -include $(DEPS)
$(I2PD): $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC)) $(I2PD): $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC)) $(ARLIB) $(ARLIB_CLIENT)
$(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS) $(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS)
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC)) $(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
@ -56,9 +59,18 @@ ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^ $(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
endif endif
$(SHLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
$(ARLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
ar -r $@ $^
$(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
ar -r $@ $^
clean: clean:
rm -rf obj rm -rf obj
$(RM) $(I2PD) $(SHLIB) $(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
LATEST_TAG=$(shell git describe --tags --abbrev=0 master) LATEST_TAG=$(shell git describe --tags --abbrev=0 master)
dist: dist:

@ -9,4 +9,4 @@ CXXFLAGS = -O2
NEEDED_CXXFLAGS = -std=c++11 NEEDED_CXXFLAGS = -std=c++11
INCFLAGS = -I/usr/include/ -I/usr/local/include/ INCFLAGS = -I/usr/include/ -I/usr/local/include/
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread

@ -33,11 +33,13 @@ ifeq ($(USE_STATIC),yes)
LDLIBS += $(LIBDIR)/libboost_filesystem.a LDLIBS += $(LIBDIR)/libboost_filesystem.a
LDLIBS += $(LIBDIR)/libboost_regex.a LDLIBS += $(LIBDIR)/libboost_regex.a
LDLIBS += $(LIBDIR)/libboost_program_options.a LDLIBS += $(LIBDIR)/libboost_program_options.a
LDLIBS += $(LIBDIR)/libcryptopp.a LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libz.a
LDLIBS += -lpthread -static-libstdc++ -static-libgcc LDLIBS += -lpthread -static-libstdc++ -static-libgcc
USE_AESNI := no USE_AESNI := no
else else
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
endif endif
# UPNP Support (miniupnpc 1.5 or 1.6) # UPNP Support (miniupnpc 1.5 or 1.6)

@ -1,12 +1,13 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <openssl/dh.h>
#include <openssl/sha.h>
#include <zlib.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include <cryptopp/dh.h> #include "Base.h"
#include <cryptopp/adler32.h>
#include "base64.h"
#include "Log.h" #include "Log.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "CryptoConst.h" #include "Crypto.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "Transports.h" #include "Transports.h"
@ -35,15 +36,9 @@ namespace transport
void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key) void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key)
{ {
CryptoPP::DH dh (elgp, elgg);
uint8_t sharedKey[256]; uint8_t sharedKey[256];
if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey)) m_DHKeysPair->Agree (pubKey, sharedKey);
{
LogPrint (eLogError, "Couldn't create shared key");
Terminate ();
return;
};
uint8_t * aesKey = key; uint8_t * aesKey = key;
if (sharedKey[0] & 0x80) if (sharedKey[0] & 0x80)
{ {
@ -97,11 +92,10 @@ namespace transport
delete m_Establisher; delete m_Establisher;
m_Establisher = nullptr; m_Establisher = nullptr;
delete m_DHKeysPair;
m_DHKeysPair = nullptr; m_DHKeysPair = nullptr;
SendTimeSyncMessage (); SendTimeSyncMessage ();
m_SendQueue.push_back (ToSharedI2NPMessage(CreateDatabaseStoreMsg ())); // we tell immediately who we are m_SendQueue.push_back (CreateDatabaseStoreMsg ()); // we tell immediately who we are
transports.PeerConnected (shared_from_this ()); transports.PeerConnected (shared_from_this ());
} }
@ -111,10 +105,10 @@ namespace transport
if (!m_DHKeysPair) if (!m_DHKeysPair)
m_DHKeysPair = transports.GetNextDHKeysPair (); m_DHKeysPair = transports.GetNextDHKeysPair ();
// send Phase1 // send Phase1
const uint8_t * x = m_DHKeysPair->publicKey; const uint8_t * x = m_DHKeysPair->GetPublicKey ();
memcpy (m_Establisher->phase1.pubKey, x, 256); memcpy (m_Establisher->phase1.pubKey, x, 256);
CryptoPP::SHA256().CalculateDigest(m_Establisher->phase1.HXxorHI, x, 256); SHA256(x, 256, m_Establisher->phase1.HXxorHI);
const uint8_t * ident = m_RemoteIdentity.GetIdentHash (); const uint8_t * ident = m_RemoteIdentity->GetIdentHash ();
for (int i = 0; i < 32; i++) for (int i = 0; i < 32; i++)
m_Establisher->phase1.HXxorHI[i] ^= ident[i]; m_Establisher->phase1.HXxorHI[i] ^= ident[i];
@ -166,8 +160,8 @@ namespace transport
{ {
// verify ident // verify ident
uint8_t digest[32]; uint8_t digest[32];
CryptoPP::SHA256().CalculateDigest(digest, m_Establisher->phase1.pubKey, 256); SHA256(m_Establisher->phase1.pubKey, 256, digest);
const uint8_t * ident = i2p::context.GetRouterInfo ().GetIdentHash (); const uint8_t * ident = i2p::context.GetIdentHash ();
for (int i = 0; i < 32; i++) for (int i = 0; i < 32; i++)
{ {
if ((m_Establisher->phase1.HXxorHI[i] ^ ident[i]) != digest[i]) if ((m_Establisher->phase1.HXxorHI[i] ^ ident[i]) != digest[i])
@ -186,12 +180,12 @@ namespace transport
{ {
if (!m_DHKeysPair) if (!m_DHKeysPair)
m_DHKeysPair = transports.GetNextDHKeysPair (); m_DHKeysPair = transports.GetNextDHKeysPair ();
const uint8_t * y = m_DHKeysPair->publicKey; const uint8_t * y = m_DHKeysPair->GetPublicKey ();
memcpy (m_Establisher->phase2.pubKey, y, 256); memcpy (m_Establisher->phase2.pubKey, y, 256);
uint8_t xy[512]; uint8_t xy[512];
memcpy (xy, m_Establisher->phase1.pubKey, 256); memcpy (xy, m_Establisher->phase1.pubKey, 256);
memcpy (xy + 256, y, 256); memcpy (xy + 256, y, 256);
CryptoPP::SHA256().CalculateDigest(m_Establisher->phase2.encrypted.hxy, xy, 512); SHA256(xy, 512, m_Establisher->phase2.encrypted.hxy);
uint32_t tsB = htobe32 (i2p::util::GetSecondsSinceEpoch ()); uint32_t tsB = htobe32 (i2p::util::GetSecondsSinceEpoch ());
m_Establisher->phase2.encrypted.timestamp = tsB; m_Establisher->phase2.encrypted.timestamp = tsB;
// TODO: fill filler // TODO: fill filler
@ -233,7 +227,7 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
// this RI is not valid // this RI is not valid
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true); i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true);
transports.ReuseDHKeysPair (m_DHKeysPair); transports.ReuseDHKeysPair (m_DHKeysPair);
m_DHKeysPair = nullptr; m_DHKeysPair = nullptr;
Terminate (); Terminate ();
@ -251,9 +245,11 @@ namespace transport
m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
// verify // verify
uint8_t xy[512]; uint8_t xy[512];
memcpy (xy, m_DHKeysPair->publicKey, 256); memcpy (xy, m_DHKeysPair->GetPublicKey (), 256);
memcpy (xy + 256, m_Establisher->phase2.pubKey, 256); memcpy (xy + 256, m_Establisher->phase2.pubKey, 256);
if (!CryptoPP::SHA256().VerifyDigest(m_Establisher->phase2.encrypted.hxy, xy, 512)) uint8_t digest[32];
SHA256 (xy, 512, digest);
if (memcmp(m_Establisher->phase2.encrypted.hxy, digest, 32))
{ {
LogPrint (eLogError, "Incorrect hash"); LogPrint (eLogError, "Incorrect hash");
transports.ReuseDHKeysPair (m_DHKeysPair); transports.ReuseDHKeysPair (m_DHKeysPair);
@ -269,13 +265,13 @@ namespace transport
{ {
auto keys = i2p::context.GetPrivateKeys (); auto keys = i2p::context.GetPrivateKeys ();
uint8_t * buf = m_ReceiveBuffer; uint8_t * buf = m_ReceiveBuffer;
htobe16buf (buf, keys.GetPublic ().GetFullLen ()); htobe16buf (buf, keys.GetPublic ()->GetFullLen ());
buf += 2; buf += 2;
buf += i2p::context.GetIdentity ().ToBuffer (buf, NTCP_BUFFER_SIZE); buf += i2p::context.GetIdentity ()->ToBuffer (buf, NTCP_BUFFER_SIZE);
uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ()); uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ());
htobuf32(buf,tsA); htobuf32(buf,tsA);
buf += 4; buf += 4;
size_t signatureLen = keys.GetPublic ().GetSignatureLen (); size_t signatureLen = keys.GetPublic ()->GetSignatureLen ();
size_t len = (buf - m_ReceiveBuffer) + signatureLen; size_t len = (buf - m_ReceiveBuffer) + signatureLen;
size_t paddingSize = len & 0x0F; // %16 size_t paddingSize = len & 0x0F; // %16
if (paddingSize > 0) if (paddingSize > 0)
@ -289,7 +285,7 @@ namespace transport
SignedData s; SignedData s;
s.Insert (m_Establisher->phase1.pubKey, 256); // x s.Insert (m_Establisher->phase1.pubKey, 256); // x
s.Insert (m_Establisher->phase2.pubKey, 256); // y s.Insert (m_Establisher->phase2.pubKey, 256); // y
s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident
s.Insert (tsA); // tsA s.Insert (tsA); // tsA
s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB
s.Sign (keys, buf); s.Sign (keys, buf);
@ -310,7 +306,7 @@ namespace transport
else else
{ {
// wait for phase4 // wait for phase4
auto signatureLen = m_RemoteIdentity.GetSignatureLen (); auto signatureLen = m_RemoteIdentity->GetSignatureLen ();
size_t paddingSize = signatureLen & 0x0F; // %16 size_t paddingSize = signatureLen & 0x0F; // %16
if (paddingSize > 0) signatureLen += (16 - paddingSize); if (paddingSize > 0) signatureLen += (16 - paddingSize);
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (), boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (),
@ -332,20 +328,20 @@ namespace transport
m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer); m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
uint8_t * buf = m_ReceiveBuffer; uint8_t * buf = m_ReceiveBuffer;
uint16_t size = bufbe16toh (buf); uint16_t size = bufbe16toh (buf);
m_RemoteIdentity.FromBuffer (buf + 2, size); SetRemoteIdentity (std::make_shared<i2p::data::IdentityEx> (buf + 2, size));
if (m_Server.FindNTCPSession (m_RemoteIdentity.GetIdentHash ())) if (m_Server.FindNTCPSession (m_RemoteIdentity->GetIdentHash ()))
{ {
LogPrint (eLogError, "NTCP session already exists"); LogPrint (eLogError, "NTCP session already exists");
Terminate (); Terminate ();
} }
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity.GetSignatureLen (); size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity->GetSignatureLen ();
size_t paddingLen = expectedSize & 0x0F; size_t paddingLen = expectedSize & 0x0F;
if (paddingLen) paddingLen = (16 - paddingLen); if (paddingLen) paddingLen = (16 - paddingLen);
if (expectedSize > NTCP_DEFAULT_PHASE3_SIZE) if (expectedSize > NTCP_DEFAULT_PHASE3_SIZE)
{ {
// we need more bytes for Phase3 // we need more bytes for Phase3
expectedSize += paddingLen; expectedSize += paddingLen;
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize), boost::asio::transfer_all (), boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize - NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (),
std::bind(&NTCPSession::HandlePhase3ExtraReceived, shared_from_this (), std::bind(&NTCPSession::HandlePhase3ExtraReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, tsB, paddingLen)); std::placeholders::_1, std::placeholders::_2, tsB, paddingLen));
} }
@ -371,7 +367,7 @@ namespace transport
void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen) void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen)
{ {
uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity.GetFullLen () + 2 /*size*/; uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity->GetFullLen () + 2 /*size*/;
uint32_t tsA = buf32toh(buf); uint32_t tsA = buf32toh(buf);
buf += 4; buf += 4;
buf += paddingLen; buf += paddingLen;
@ -388,7 +384,6 @@ namespace transport
Terminate (); Terminate ();
return; return;
} }
m_RemoteIdentity.DropVerifier ();
SendPhase4 (tsA, tsB); SendPhase4 (tsA, tsB);
} }
@ -398,11 +393,11 @@ namespace transport
SignedData s; SignedData s;
s.Insert (m_Establisher->phase1.pubKey, 256); // x s.Insert (m_Establisher->phase1.pubKey, 256); // x
s.Insert (m_Establisher->phase2.pubKey, 256); // y s.Insert (m_Establisher->phase2.pubKey, 256); // y
s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident
s.Insert (tsA); // tsA s.Insert (tsA); // tsA
s.Insert (tsB); // tsB s.Insert (tsB); // tsB
auto keys = i2p::context.GetPrivateKeys (); auto keys = i2p::context.GetPrivateKeys ();
auto signatureLen = keys.GetPublic ().GetSignatureLen (); auto signatureLen = keys.GetPublic ()->GetSignatureLen ();
s.Sign (keys, m_ReceiveBuffer); s.Sign (keys, m_ReceiveBuffer);
size_t paddingSize = signatureLen & 0x0F; // %16 size_t paddingSize = signatureLen & 0x0F; // %16
if (paddingSize > 0) signatureLen += (16 - paddingSize); if (paddingSize > 0) signatureLen += (16 - paddingSize);
@ -440,7 +435,7 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
// this router doesn't like us // this router doesn't like us
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true); i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true);
Terminate (); Terminate ();
} }
} }
@ -452,7 +447,7 @@ namespace transport
SignedData s; SignedData s;
s.Insert (m_Establisher->phase1.pubKey, 256); // x s.Insert (m_Establisher->phase1.pubKey, 256); // x
s.Insert (m_Establisher->phase2.pubKey, 256); // y s.Insert (m_Establisher->phase2.pubKey, 256); // y
s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident s.Insert (i2p::context.GetIdentHash (), 32); // ident
s.Insert (tsA); // tsA s.Insert (tsA); // tsA
s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB
@ -462,7 +457,6 @@ namespace transport
Terminate (); Terminate ();
return; return;
} }
m_RemoteIdentity.DropVerifier ();
LogPrint (eLogInfo, "NTCP session to ", m_Socket.remote_endpoint (), " connected"); LogPrint (eLogInfo, "NTCP session to ", m_Socket.remote_endpoint (), " connected");
Connected (); Connected ();
@ -583,7 +577,9 @@ namespace transport
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
{ {
// we have a complete I2NP message // we have a complete I2NP message
if (CryptoPP::Adler32().VerifyDigest (m_NextMessage->buf + m_NextMessageOffset - 4, m_NextMessage->buf, m_NextMessageOffset - 4)) uint8_t checksum[4];
htobe32buf (checksum, adler32 (adler32 (0, Z_NULL, 0), m_NextMessage->buf, m_NextMessageOffset - 4));
if (!memcmp (m_NextMessage->buf + m_NextMessageOffset - 4, checksum, 4))
m_Handler.PutNextMessage (m_NextMessage); m_Handler.PutNextMessage (m_NextMessage);
else else
LogPrint (eLogWarning, "Incorrect adler checksum of NTCP message. Dropped"); LogPrint (eLogWarning, "Incorrect adler checksum of NTCP message. Dropped");
@ -625,7 +621,7 @@ namespace transport
int padding = 0; int padding = 0;
if (rem > 0) padding = 16 - rem; if (rem > 0) padding = 16 - rem;
// TODO: fill padding // TODO: fill padding
CryptoPP::Adler32().CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding); htobe32buf (sendBuffer + len + 2 + padding, adler32 (adler32 (0, Z_NULL, 0), sendBuffer, len + 2+ padding));
int l = len + padding + 6; int l = len + padding + 6;
m_Encryption.Encrypt(sendBuffer, l, sendBuffer); m_Encryption.Encrypt(sendBuffer, l, sendBuffer);
@ -799,19 +795,19 @@ namespace transport
void NTCPServer::AddNTCPSession (std::shared_ptr<NTCPSession> session) void NTCPServer::AddNTCPSession (std::shared_ptr<NTCPSession> session)
{ {
if (session) if (session && session->GetRemoteIdentity ())
{ {
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex); std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session; m_NTCPSessions[session->GetRemoteIdentity ()->GetIdentHash ()] = session;
} }
} }
void NTCPServer::RemoveNTCPSession (std::shared_ptr<NTCPSession> session) void NTCPServer::RemoveNTCPSession (std::shared_ptr<NTCPSession> session)
{ {
if (session) if (session && session->GetRemoteIdentity ())
{ {
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex); std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ()); m_NTCPSessions.erase (session->GetRemoteIdentity ()->GetIdentHash ());
} }
} }
@ -914,7 +910,7 @@ namespace transport
{ {
LogPrint (eLogError, "Connect error: ", ecode.message ()); LogPrint (eLogError, "Connect error: ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ().GetIdentHash (), true); i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
conn->Terminate (); conn->Terminate ();
} }
else else

@ -7,9 +7,7 @@
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <cryptopp/modes.h> #include "Crypto.h"
#include <cryptopp/aes.h>
#include "aes.h"
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"

@ -3,8 +3,9 @@
#include <fstream> #include <fstream>
#include <vector> #include <vector>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <cryptopp/gzip.h> #include <openssl/rand.h>
#include "base64.h" #include <zlib.h>
#include "Base.h"
#include "Log.h" #include "Log.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
@ -38,23 +39,8 @@ namespace data
{ {
Load (); Load ();
if (m_RouterInfos.size () < 25) // reseed if # of router less than 50 if (m_RouterInfos.size () < 25) // reseed if # of router less than 50
{
// try SU3 first
Reseed (); Reseed ();
// deprecated
if (m_Reseeder)
{
// if still not enough download .dat files
int reseedRetries = 0;
while (m_RouterInfos.size () < 25 && reseedRetries < 5)
{
m_Reseeder->reseedNow();
reseedRetries++;
Load ();
}
}
}
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&NetDb::Run, this)); m_Thread = new std::thread (std::bind (&NetDb::Run, this));
} }
@ -245,6 +231,12 @@ namespace data
return nullptr; return nullptr;
} }
std::shared_ptr<RouterProfile> NetDb::FindRouterProfile (const IdentHash& ident) const
{
auto router = FindRouter (ident);
return router ? router->GetProfile () : nullptr;
}
void NetDb::SetUnreachable (const IdentHash& ident, bool unreachable) void NetDb::SetUnreachable (const IdentHash& ident, bool unreachable)
{ {
auto it = m_RouterInfos.find (ident); auto it = m_RouterInfos.find (ident);
@ -518,25 +510,10 @@ namespace data
LogPrint ("Invalid RouterInfo length ", (int)size); LogPrint ("Invalid RouterInfo length ", (int)size);
return; return;
} }
try uint8_t uncompressed[2048];
{ size_t uncompressedSize = m_Inflator.Inflate (buf + offset, size, uncompressed, 2048);
CryptoPP::Gunzip decompressor; if (uncompressedSize)
decompressor.Put (buf + offset, size); AddRouterInfo (ident, uncompressed, uncompressedSize);
decompressor.MessageEnd();
uint8_t uncompressed[2048];
size_t uncomressedSize = decompressor.MaxRetrievable ();
if (uncomressedSize <= 2048)
{
decompressor.Get (uncompressed, uncomressedSize);
AddRouterInfo (ident, uncompressed, uncomressedSize);
}
else
LogPrint ("Invalid RouterInfo uncomressed length ", (int)uncomressedSize);
}
catch (CryptoPP::Exception& ex)
{
LogPrint (eLogError, "DatabaseStore: ", ex.what ());
}
} }
} }
@ -575,7 +552,7 @@ namespace data
{ {
i2p::tunnel::eDeliveryTypeRouter, i2p::tunnel::eDeliveryTypeRouter,
nextFloodfill->GetIdentHash (), 0, nextFloodfill->GetIdentHash (), 0,
ToSharedI2NPMessage (CreateDatabaseStoreMsg ()) CreateDatabaseStoreMsg ()
}); });
// request destination // request destination
@ -679,7 +656,7 @@ namespace data
excludedRouters.insert (r->GetIdentHash ()); excludedRouters.insert (r->GetIdentHash ());
} }
} }
replyMsg = ToSharedI2NPMessage (CreateDatabaseSearchReply (ident, routers)); replyMsg = CreateDatabaseSearchReply (ident, routers);
} }
else else
{ {
@ -692,7 +669,7 @@ namespace data
LogPrint ("Requested RouterInfo ", key, " found"); LogPrint ("Requested RouterInfo ", key, " found");
router->LoadBuffer (); router->LoadBuffer ();
if (router->GetBuffer ()) if (router->GetBuffer ())
replyMsg = ToSharedI2NPMessage (CreateDatabaseStoreMsg (router)); replyMsg = CreateDatabaseStoreMsg (router);
} }
} }
@ -703,7 +680,7 @@ namespace data
if (leaseSet) // we don't send back our LeaseSets if (leaseSet) // we don't send back our LeaseSets
{ {
LogPrint ("Requested LeaseSet ", key, " found"); LogPrint ("Requested LeaseSet ", key, " found");
replyMsg = ToSharedI2NPMessage (CreateDatabaseStoreMsg (leaseSet)); replyMsg = CreateDatabaseStoreMsg (leaseSet);
} }
} }
@ -716,7 +693,7 @@ namespace data
excludedRouters.insert (excluded); excludedRouters.insert (excluded);
excluded += 32; excluded += 32;
} }
replyMsg = ToSharedI2NPMessage (CreateDatabaseSearchReply (ident, GetClosestFloodfills (ident, 3, excludedRouters))); replyMsg = CreateDatabaseSearchReply (ident, GetClosestFloodfills (ident, 3, excludedRouters));
} }
} }
@ -756,14 +733,13 @@ namespace data
auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr; auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr;
bool throughTunnels = outbound && inbound; bool throughTunnels = outbound && inbound;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint8_t randomHash[32]; uint8_t randomHash[32];
std::vector<i2p::tunnel::TunnelMessageBlock> msgs; std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
std::set<const RouterInfo *> floodfills; std::set<const RouterInfo *> floodfills;
LogPrint ("Exploring new ", numDestinations, " routers ..."); LogPrint ("Exploring new ", numDestinations, " routers ...");
for (int i = 0; i < numDestinations; i++) for (int i = 0; i < numDestinations; i++)
{ {
rnd.GenerateBlock (randomHash, 32); RAND_bytes (randomHash, 32);
auto dest = m_Requests.CreateRequest (randomHash, true); // exploratory auto dest = m_Requests.CreateRequest (randomHash, true); // exploratory
if (!dest) if (!dest)
{ {
@ -782,7 +758,7 @@ namespace data
{ {
i2p::tunnel::eDeliveryTypeRouter, i2p::tunnel::eDeliveryTypeRouter,
floodfill->GetIdentHash (), 0, floodfill->GetIdentHash (), 0,
ToSharedI2NPMessage (CreateDatabaseStoreMsg ()) // tell floodfill about us CreateDatabaseStoreMsg () // tell floodfill about us
}); });
msgs.push_back (i2p::tunnel::TunnelMessageBlock msgs.push_back (i2p::tunnel::TunnelMessageBlock
{ {
@ -809,9 +785,10 @@ namespace data
auto floodfill = GetClosestFloodfill (i2p::context.GetRouterInfo ().GetIdentHash (), excluded); auto floodfill = GetClosestFloodfill (i2p::context.GetRouterInfo ().GetIdentHash (), excluded);
if (floodfill) if (floodfill)
{ {
uint32_t replyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); uint32_t replyToken;
LogPrint ("Publishing our RouterInfo to ", floodfill->GetIdentHashAbbreviation (), ". reply token=", replyToken); RAND_bytes ((uint8_t *)&replyToken, 4);
transports.SendMessage (floodfill->GetIdentHash (), ToSharedI2NPMessage (CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken))); LogPrint ("Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken);
transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken));
excluded.insert (floodfill->GetIdentHash ()); excluded.insert (floodfill->GetIdentHash ());
} }
} }
@ -868,8 +845,8 @@ namespace data
template<typename Filter> template<typename Filter>
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (Filter filter) const std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (Filter filter) const
{ {
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); if (!m_RouterInfos.size ()) return 0;
uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1); uint32_t ind = rand () % m_RouterInfos.size ();
for (int j = 0; j < 2; j++) for (int j = 0; j < 2; j++)
{ {
uint32_t i = 0; uint32_t i = 0;

@ -9,6 +9,7 @@
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include "Base.h"
#include "Queue.h" #include "Queue.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "RouterInfo.h" #include "RouterInfo.h"
@ -38,6 +39,7 @@ namespace data
void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from); void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const; std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const; std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const;
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr); void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);
@ -91,6 +93,7 @@ namespace data
std::thread * m_Thread; std::thread * m_Thread;
i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg
GzipInflator m_Inflator;
Reseeder * m_Reseeder; Reseeder * m_Reseeder;
friend class NetDbRequests; friend class NetDbRequests;

@ -11,21 +11,21 @@ namespace data
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router, std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel) std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{ {
I2NPMessage * msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
&m_ExcludedPeers); &m_ExcludedPeers);
m_ExcludedPeers.insert (router->GetIdentHash ()); m_ExcludedPeers.insert (router->GetIdentHash ());
m_CreationTime = i2p::util::GetSecondsSinceEpoch (); m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
return ToSharedI2NPMessage (msg); return msg;
} }
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (const IdentHash& floodfill) std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (const IdentHash& floodfill)
{ {
I2NPMessage * msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers); i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill); m_ExcludedPeers.insert (floodfill);
m_CreationTime = i2p::util::GetSecondsSinceEpoch (); m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
return ToSharedI2NPMessage (msg); return msg;
} }
void RequestedDestination::ClearExcludedPeers () void RequestedDestination::ClearExcludedPeers ()

@ -1,8 +1,9 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp> #include <boost/property_tree/ini_parser.hpp>
#include "base64.h" #include "Base.h"
#include "util.h" #include "util.h"
#include "Log.h"
#include "Profiling.h" #include "Profiling.h"
namespace i2p namespace i2p

@ -3,19 +3,17 @@
#include <sstream> #include <sstream>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp> #include <boost/asio.hpp>
#include <cryptopp/asn.h> #include <boost/asio/ssl.hpp>
#include <cryptopp/base64.h> #include <openssl/bn.h>
#include <cryptopp/crc.h> #include <openssl/ssl.h>
#include <cryptopp/hmac.h> #include <openssl/err.h>
#include <cryptopp/zinflate.h> #include <zlib.h>
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include <cryptopp/arc4.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Reseed.h" #include "Reseed.h"
#include "Log.h" #include "Log.h"
#include "Identity.h" #include "Identity.h"
#include "CryptoConst.h" #include "Crypto.h"
#include "NetDb.h" #include "NetDb.h"
#include "util.h" #include "util.h"
@ -24,27 +22,18 @@ namespace i2p
{ {
namespace data namespace data
{ {
static std::vector<std::string> httpsReseedHostList =
static std::vector<std::string> httpReseedHostList = { {
"http://netdb.i2p2.no/", // only SU3 (v2) support "https://reseed.i2p-projekt.de/", // Only HTTPS
"http://i2p-netdb.innovatio.no/", "https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v3) support
"http://193.150.121.66/netDb/" "https://i2p.mooo.com/netDb/",
}; "https://netdb.i2p2.no/", // Only SU3 (v3) support, SNI required
"https://us.reseed.i2p2.no:444/",
static std::vector<std::string> httpsReseedHostList = { "https://uk.reseed.i2p2.no:444/",
// "https://193.150.121.66/netDb/", // unstable "https://reseed.i2p.vzaws.com:8443/", // Only SU3 (v3) support
// "https://i2p-netdb.innovatio.no/",// Vuln to POODLE "https://user.mx24.eu/", // Only HTTPS and SU3 (v3) support
"https://netdb.i2p2.no/", // Only SU3 (v2) support "https://ieb9oopo.mooo.com/" // Only HTTPS and SU3 (v3) support
"https://reseed.i2p-projekt.de/", // Only HTTPS };
//"https://cowpuncher.drollette.com/netdb/", // returns error
"https://netdb.rows.io:444/",
"https://uk.reseed.i2p2.no:444/"
// following hosts are fine but don't support AES256
/*"https://i2p.mooo.com/netDb/",
"https://link.mx24.eu/", // Only HTTPS and SU3 (v2) support
"https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v2) support
"https://ieb9oopo.mooo.com/" // Only HTTPS and SU3 (v2) support*/
};
Reseeder::Reseeder() Reseeder::Reseeder()
{ {
@ -54,75 +43,11 @@ namespace data
{ {
} }
bool Reseeder::reseedNow()
{
// This method is deprecated
try
{
std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())];
LogPrint("Reseeding from ", reseedHost);
std::string content = i2p::util::http::httpRequest(reseedHost);
if (content == "")
{
LogPrint("Reseed failed");
return false;
}
boost::regex e("<\\s*A\\s+[^>]*href\\s*=\\s*\"([^\"]*)\"", boost::regex::normal | boost::regbase::icase);
boost::sregex_token_iterator i(content.begin(), content.end(), e, 1);
boost::sregex_token_iterator j;
//TODO: Ugly code, try to clean up.
//TODO: Try to reduce N number of variables
std::string name;
std::string routerInfo;
std::string tmpUrl;
std::string filename;
std::string ignoreFileSuffix = ".su3";
boost::filesystem::path root = i2p::util::filesystem::GetDataDir();
while (i != j)
{
name = *i++;
if (name.find(ignoreFileSuffix)!=std::string::npos)
continue;
LogPrint("Downloading ", name);
tmpUrl = reseedHost;
tmpUrl.append(name);
routerInfo = i2p::util::http::httpRequest(tmpUrl);
if (routerInfo.size()==0)
continue;
filename = root.string();
#ifndef _WIN32
filename += "/netDb/r";
#else
filename += "\\netDb\\r";
#endif
filename += name.at(11); // first char in id
#ifndef _WIN32
filename.append("/");
#else
filename.append("\\");
#endif
filename.append(name.c_str());
std::ofstream outfile (filename, std::ios::binary);
outfile << routerInfo;
outfile.close();
}
return true;
}
catch (std::exception& ex)
{
//TODO: error reporting
return false;
}
return false;
}
int Reseeder::ReseedNowSU3 () int Reseeder::ReseedNowSU3 ()
{ {
CryptoPP::AutoSeededRandomPool rnd; auto ind = rand () % httpsReseedHostList.size ();
auto ind = rnd.GenerateWord32 (0, httpReseedHostList.size() - 1 + httpsReseedHostList.size () - 1); std::string& reseedHost = httpsReseedHostList[ind];
std::string reseedHost = (ind < httpReseedHostList.size()) ? httpReseedHostList[ind] : return ReseedFromSU3 (reseedHost, true);
httpsReseedHostList[ind - httpReseedHostList.size()];
return ReseedFromSU3 (reseedHost, ind >= httpReseedHostList.size());
} }
int Reseeder::ReseedFromSU3 (const std::string& host, bool https) int Reseeder::ReseedFromSU3 (const std::string& host, bool https)
@ -221,10 +146,27 @@ namespace data
uint8_t * signature = new uint8_t[signatureLength]; uint8_t * signature = new uint8_t[signatureLength];
s.read ((char *)signature, signatureLength); s.read ((char *)signature, signatureLength);
// RSA-raw // RSA-raw
i2p::crypto::RSASHA5124096RawVerifier verifier(it->second); {
verifier.Update (tbs, tbsLen); // calculate digest
if (!verifier.Verify (signature)) uint8_t digest[64];
LogPrint (eLogWarning, "SU3 signature verification failed"); SHA512 (tbs, tbsLen, digest);
// encrypt signature
BN_CTX * bnctx = BN_CTX_new ();
BIGNUM * s = BN_new (), * n = BN_new ();
BN_bin2bn (signature, signatureLength, s);
BN_bin2bn (it->second, i2p::crypto::RSASHA5124096_KEY_LENGTH, n);
BN_mod_exp (s, s, i2p::crypto::rsae, n, bnctx); // s = s^e mod n
uint8_t * enSigBuf = new uint8_t[signatureLength];
i2p::crypto::bn2buf (s, enSigBuf, signatureLength);
// digest is right aligned
// we can't use RSA_verify due wrong padding in SU3
if (memcmp (enSigBuf + (signatureLength - 64), digest, 64))
LogPrint (eLogWarning, "SU3 signature verification failed");
delete[] enSigBuf;
BN_free (s); BN_free (n);
BN_CTX_free (bnctx);
}
delete[] signature; delete[] signature;
delete[] tbs; delete[] tbs;
s.seekg (pos, std::ios::beg); s.seekg (pos, std::ios::beg);
@ -255,8 +197,9 @@ namespace data
compressionMethod = le16toh (compressionMethod); compressionMethod = le16toh (compressionMethod);
s.seekg (4, std::ios::cur); // skip fields we don't care about s.seekg (4, std::ios::cur); // skip fields we don't care about
uint32_t compressedSize, uncompressedSize; uint32_t compressedSize, uncompressedSize;
uint8_t crc32[4]; uint32_t crc_32;
s.read ((char *)crc32, 4); s.read ((char *)&crc_32, 4);
crc_32 = le32toh (crc_32);
s.read ((char *)&compressedSize, 4); s.read ((char *)&compressedSize, 4);
compressedSize = le32toh (compressedSize); compressedSize = le32toh (compressedSize);
s.read ((char *)&uncompressedSize, 4); s.read ((char *)&uncompressedSize, 4);
@ -278,9 +221,9 @@ namespace data
{ {
LogPrint (eLogError, "SU3 archive data descriptor not found"); LogPrint (eLogError, "SU3 archive data descriptor not found");
return numFiles; return numFiles;
} }
s.read ((char *)&crc_32, 4);
s.read ((char *)crc32, 4); crc_32 = le32toh (crc_32);
s.read ((char *)&compressedSize, 4); s.read ((char *)&compressedSize, 4);
compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data
s.read ((char *)&uncompressedSize, 4); s.read ((char *)&uncompressedSize, 4);
@ -301,25 +244,31 @@ namespace data
s.read ((char *)compressed, compressedSize); s.read ((char *)compressed, compressedSize);
if (compressionMethod) // we assume Deflate if (compressionMethod) // we assume Deflate
{ {
CryptoPP::Inflator decompressor; z_stream inflator;
decompressor.Put (compressed, compressedSize); memset (&inflator, 0, sizeof (inflator));
decompressor.MessageEnd(); inflateInit2 (&inflator, -MAX_WBITS); // no zlib header
if (decompressor.MaxRetrievable () <= uncompressedSize) uint8_t * uncompressed = new uint8_t[uncompressedSize];
{ inflator.next_in = compressed;
uint8_t * uncompressed = new uint8_t[uncompressedSize]; inflator.avail_in = compressedSize;
decompressor.Get (uncompressed, uncompressedSize); inflator.next_out = uncompressed;
if (CryptoPP::CRC32().VerifyDigest (crc32, uncompressed, uncompressedSize)) inflator.avail_out = uncompressedSize;
int err;
if ((err = inflate (&inflator, Z_SYNC_FLUSH)) >= 0)
{
uncompressedSize -= inflator.avail_out;
if (crc32 (0, uncompressed, uncompressedSize) == crc_32)
{ {
i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize); i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize);
numFiles++; numFiles++;
} }
else else
LogPrint (eLogError, "CRC32 verification failed"); LogPrint (eLogError, "CRC32 verification failed");
delete[] uncompressed; }
}
else else
LogPrint (eLogError, "Actual uncompressed size ", decompressor.MaxRetrievable (), " exceed ", uncompressedSize, " from header"); LogPrint (eLogError, "decompression error ", err);
} delete[] uncompressed;
inflateEnd (&inflator);
}
else // no compression else // no compression
{ {
i2p::data::netdb.AddRouterInfo (compressed, compressedSize); i2p::data::netdb.AddRouterInfo (compressed, compressedSize);
@ -362,111 +311,33 @@ namespace data
return false; return false;
} }
const char CERTIFICATE_HEADER[] = "-----BEGIN CERTIFICATE-----";
const char CERTIFICATE_FOOTER[] = "-----END CERTIFICATE-----";
void Reseeder::LoadCertificate (const std::string& filename) void Reseeder::LoadCertificate (const std::string& filename)
{ {
std::ifstream s(filename, std::ifstream::binary); SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
if (s.is_open ()) int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
{ if (ret)
s.seekg (0, std::ios::end); {
size_t len = s.tellg (); SSL * ssl = SSL_new (ctx);
s.seekg (0, std::ios::beg); X509 * cert = SSL_get_certificate (ssl);
char buf[2048]; // verify
s.read (buf, len); if (cert)
std::string cert (buf, len); {
// assume file in pem format // extract issuer name
auto pos1 = cert.find (CERTIFICATE_HEADER); char name[100];
auto pos2 = cert.find (CERTIFICATE_FOOTER); X509_NAME_oneline (X509_get_issuer_name(cert), name, 100);
if (pos1 == std::string::npos || pos2 == std::string::npos) // extract RSA key (we need n only, e = 65537)
{ RSA * key = X509_get_pubkey (cert)->pkey.rsa;
LogPrint (eLogError, "Malformed certificate file"); PublicKey value;
return; i2p::crypto::bn2buf (key->n, value, 512);
m_SigningKeys[name] = value;
} }
pos1 += strlen (CERTIFICATE_HEADER); SSL_free (ssl);
pos2 -= pos1; }
std::string base64 = cert.substr (pos1, pos2);
CryptoPP::ByteQueue queue;
CryptoPP::Base64Decoder decoder; // regular base64 rather than I2P
decoder.Attach (new CryptoPP::Redirector (queue));
decoder.Put ((const uint8_t *)base64.data(), base64.length());
decoder.MessageEnd ();
LoadCertificate (queue);
}
else else
LogPrint (eLogError, "Can't open certificate file ", filename); LogPrint (eLogError, "Can't open certificate file ", filename);
SSL_CTX_free (ctx);
} }
std::string Reseeder::LoadCertificate (CryptoPP::ByteQueue& queue)
{
// extract X.509
CryptoPP::BERSequenceDecoder x509Cert (queue);
CryptoPP::BERSequenceDecoder tbsCert (x509Cert);
// version
uint32_t ver;
CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED);
CryptoPP::BERDecodeUnsigned<uint32_t>(context, ver, CryptoPP::INTEGER);
// serial
CryptoPP::Integer serial;
serial.BERDecode(tbsCert);
// signature
CryptoPP::BERSequenceDecoder signature (tbsCert);
signature.SkipAll();
// issuer
std::string name;
CryptoPP::BERSequenceDecoder issuer (tbsCert);
{
CryptoPP::BERSetDecoder c (issuer); c.SkipAll();
CryptoPP::BERSetDecoder st (issuer); st.SkipAll();
CryptoPP::BERSetDecoder l (issuer); l.SkipAll();
CryptoPP::BERSetDecoder o (issuer); o.SkipAll();
CryptoPP::BERSetDecoder ou (issuer); ou.SkipAll();
CryptoPP::BERSetDecoder cn (issuer);
{
CryptoPP::BERSequenceDecoder attributes (cn);
{
CryptoPP::BERGeneralDecoder ident(attributes, CryptoPP::OBJECT_IDENTIFIER);
ident.SkipAll ();
CryptoPP::BERDecodeTextString (attributes, name, CryptoPP::UTF8_STRING);
}
}
}
issuer.SkipAll();
// validity
CryptoPP::BERSequenceDecoder validity (tbsCert);
validity.SkipAll();
// subject
CryptoPP::BERSequenceDecoder subject (tbsCert);
subject.SkipAll();
// public key
CryptoPP::BERSequenceDecoder publicKey (tbsCert);
{
CryptoPP::BERSequenceDecoder ident (publicKey);
ident.SkipAll ();
CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING);
key.Skip (1); // FIXME: probably bug in crypto++
CryptoPP::BERSequenceDecoder keyPair (key);
CryptoPP::Integer n;
n.BERDecode (keyPair);
if (name.length () > 0)
{
PublicKey value;
n.Encode (value, 512);
m_SigningKeys[name] = value;
}
else
LogPrint (eLogWarning, "Unknown issuer. Skipped");
}
publicKey.SkipAll();
tbsCert.SkipAll();
x509Cert.SkipAll();
return name;
}
void Reseeder::LoadCertificates () void Reseeder::LoadCertificates ()
{ {
boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed"; boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed";
@ -494,439 +365,50 @@ namespace data
{ {
i2p::util::http::url u(address); i2p::util::http::url u(address);
if (u.port_ == 80) u.port_ = 443; if (u.port_ == 80) u.port_ = 443;
TlsSession session (u.host_, u.port_);
if (session.IsEstablished ())
{
// send request
std::stringstream ss;
ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n";
session.Send ((uint8_t *)ss.str ().c_str (), ss.str ().length ());
// read response boost::asio::io_service service;
std::stringstream rs; boost::system::error_code ecode;
while (session.Receive (rs)) auto it = boost::asio::ip::tcp::resolver(service).resolve (
; boost::asio::ip::tcp::resolver::query (u.host_, std::to_string (u.port_)), ecode);
return i2p::util::http::GetHttpContent (rs); if (!ecode)
} {
else boost::asio::ssl::context ctx(service, boost::asio::ssl::context::tlsv12);
return ""; ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
} boost::asio::ssl::stream<boost::asio::ip::tcp::socket> s(service, ctx);
s.lowest_layer().connect (*it, ecode);
//------------------------------------------------------------- if (!ecode)
template<class Hash>
class TlsCipherMAC: public TlsCipher
{
public:
TlsCipherMAC (const uint8_t * keys): m_Seqn (0)
{
memcpy (m_MacKey, keys, Hash::DIGESTSIZE);
}
void CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac)
{
uint8_t header[13]; // seqn (8) + type (1) + version (2) + length (2)
htobe64buf (header, m_Seqn);
header[8] = type; header[9] = 3; header[10] = 3; // 3,3 means TLS 1.2
htobe16buf (header + 11, len);
CryptoPP::HMAC<Hash> hmac (m_MacKey, Hash::DIGESTSIZE);
hmac.Update (header, 13);
hmac.Update (buf, len);
hmac.Final (mac);
m_Seqn++;
}
private:
uint64_t m_Seqn;
uint8_t m_MacKey[Hash::DIGESTSIZE]; // client
};
template<class Hash>
class TlsCipher_AES_256_CBC: public TlsCipherMAC<Hash>
{
public:
TlsCipher_AES_256_CBC (const uint8_t * keys): TlsCipherMAC<Hash> (keys)
{
m_Encryption.SetKey (keys + 2*Hash::DIGESTSIZE);
m_Decryption.SetKey (keys + 2*Hash::DIGESTSIZE + 32);
}
size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out)
{
size_t size = 0;
m_Rnd.GenerateBlock (out, 16); // iv
size += 16;
m_Encryption.SetIV (out);
memcpy (out + size, in, len);
size += len;
memcpy (out + size, mac, Hash::DIGESTSIZE);
size += Hash::DIGESTSIZE;
uint8_t paddingSize = size + 1;
paddingSize &= 0x0F; // %16
if (paddingSize > 0) paddingSize = 16 - paddingSize;
memset (out + size, paddingSize, paddingSize + 1); // paddind and last byte are equal to padding size
size += paddingSize + 1;
m_Encryption.Encrypt (out + 16, size - 16, out + 16);
return size;
}
size_t Decrypt (uint8_t * buf, size_t len) // payload is buf + 16
{
m_Decryption.SetIV (buf);
m_Decryption.Decrypt (buf + 16, len - 16, buf + 16);
return len - 16 - Hash::DIGESTSIZE - buf[len -1] - 1; // IV(16), mac(32 or 20) and padding
}
size_t GetIVSize () const { return 16; };
private:
CryptoPP::AutoSeededRandomPool m_Rnd;
i2p::crypto::CBCEncryption m_Encryption;
i2p::crypto::CBCDecryption m_Decryption;
};
class TlsCipher_RC4_SHA: public TlsCipherMAC<CryptoPP::SHA1>
{
public:
TlsCipher_RC4_SHA (const uint8_t * keys): TlsCipherMAC (keys)
{ {
m_Encryption.SetKey (keys + 40, 16); // 20 + 20 s.handshake (boost::asio::ssl::stream_base::client, ecode);
m_Decryption.SetKey (keys + 56, 16); // 20 + 20 + 16 if (!ecode)
{
LogPrint (eLogInfo, "Connected to ", u.host_, ":", u.port_);
// send request
std::stringstream ss;
ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n";
s.write_some (boost::asio::buffer (ss.str ()));
// read response
std::stringstream rs;
char response[1024]; size_t l = 0;
do
{
l = s.read_some (boost::asio::buffer (response, 1024), ecode);
if (l) rs.write (response, l);
}
while (!ecode && l);
// process response
return i2p::util::http::GetHttpContent (rs);
}
else
LogPrint (eLogError, "SSL handshake failed: ", ecode.message ());
} }
else
size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out) LogPrint (eLogError, "Couldn't connect to ", u.host_, ": ", ecode.message ());
{
memcpy (out, in, len);
memcpy (out + len, mac, 20);
m_Encryption.ProcessData (out, out, len + 20);
return len + 20;
}
size_t Decrypt (uint8_t * buf, size_t len)
{
m_Decryption.ProcessData (buf, buf, len);
return len - 20;
}
private:
CryptoPP::Weak1::ARC4 m_Encryption, m_Decryption;
};
TlsSession::TlsSession (const std::string& host, int port):
m_IsEstablished (false), m_Cipher (nullptr)
{
m_Site.connect(host, boost::lexical_cast<std::string>(port));
if (m_Site.good ())
Handshake ();
else
LogPrint (eLogError, "Can't connect to ", host, ":", port);
}
TlsSession::~TlsSession ()
{
delete m_Cipher;
}
void TlsSession::Handshake ()
{
static uint8_t clientHello[] =
{
0x16, // handshake
0x03, 0x03, // version (TLS 1.2)
0x00, 0x33, // length of handshake
// handshake
0x01, // handshake type (client hello)
0x00, 0x00, 0x2F, // length of handshake payload
// client hello
0x03, 0x03, // highest version supported (TLS 1.2)
0x45, 0xFA, 0x01, 0x19, 0x74, 0x55, 0x18, 0x36,
0x42, 0x05, 0xC1, 0xDD, 0x4A, 0x21, 0x80, 0x80,
0xEC, 0x37, 0x11, 0x93, 0x16, 0xF4, 0x66, 0x00,
0x12, 0x67, 0xAB, 0xBA, 0xFF, 0x29, 0x13, 0x9E, // 32 random bytes
0x00, // session id length
0x00, 0x06, // chiper suites length
0x00, 0x3D, // RSA_WITH_AES_256_CBC_SHA256
0x00, 0x35, // RSA_WITH_AES_256_CBC_SHA
0x00, 0x05, // RSA_WITH_RC4_128_SHA
0x01, // compression methods length
0x00, // no compression
0x00, 0x00 // extensions length
};
static uint8_t changeCipherSpecs[] =
{
0x14, // change cipher specs
0x03, 0x03, // version (TLS 1.2)
0x00, 0x01, // length
0x01 // type
};
// send ClientHello
m_Site.write ((char *)clientHello, sizeof (clientHello));
m_FinishedHash.Update (clientHello + 5, sizeof (clientHello) - 5);
// read ServerHello
uint8_t type;
m_Site.read ((char *)&type, 1);
uint16_t version;
m_Site.read ((char *)&version, 2);
uint16_t length;
m_Site.read ((char *)&length, 2);
length = be16toh (length);
char * serverHello = new char[length];
m_Site.read (serverHello, length);
m_FinishedHash.Update ((uint8_t *)serverHello, length);
uint8_t serverRandom[32];
if (serverHello[0] == 0x02) // handshake type server hello
memcpy (serverRandom, serverHello + 6, 32);
else
LogPrint (eLogError, "Unexpected handshake type ", (int)serverHello[0]);
uint8_t sessionIDLen = serverHello[38]; // 6 + 32
char * cipherSuite = serverHello + 39 + sessionIDLen;
if (cipherSuite[1] == 0x3D || cipherSuite[1] == 0x35 || cipherSuite[1] == 0x05)
m_IsEstablished = true;
else
LogPrint (eLogError, "Unsupported cipher ", (int)cipherSuite[0], ",", (int)cipherSuite[1]);
// read Certificate
m_Site.read ((char *)&type, 1);
m_Site.read ((char *)&version, 2);
m_Site.read ((char *)&length, 2);
length = be16toh (length);
char * certificate = new char[length];
m_Site.read (certificate, length);
m_FinishedHash.Update ((uint8_t *)certificate, length);
CryptoPP::RSA::PublicKey publicKey;
// 0 - handshake type
// 1 - 3 - handshake payload length
// 4 - 6 - length of array of certificates
// 7 - 9 - length of certificate
if (certificate[0] == 0x0B) // handshake type certificate
publicKey = ExtractPublicKey ((uint8_t *)certificate + 10, length - 10);
else
LogPrint (eLogError, "Unexpected handshake type ", (int)certificate[0]);
// read ServerHelloDone
m_Site.read ((char *)&type, 1);
m_Site.read ((char *)&version, 2);
m_Site.read ((char *)&length, 2);
length = be16toh (length);
char * serverHelloDone = new char[length];
m_Site.read (serverHelloDone, length);
m_FinishedHash.Update ((uint8_t *)serverHelloDone, length);
if (serverHelloDone[0] != 0x0E) // handshake type hello done
LogPrint (eLogError, "Unexpected handshake type ", (int)serverHelloDone[0]);
// our turn now
// generate secret key
uint8_t secret[48];
secret[0] = 3; secret[1] = 3; // version
CryptoPP::AutoSeededRandomPool rnd;
rnd.GenerateBlock (secret + 2, 46); // 46 random bytes
// encrypt RSA
CryptoPP::RSAES_PKCS1v15_Encryptor encryptor(publicKey);
size_t encryptedLen = encryptor.CiphertextLength (48); // number of bytes for encrypted 48 bytes, usually 256 (2048 bits key)
uint8_t * encrypted = new uint8_t[encryptedLen + 2]; // + 2 bytes for length
htobe16buf (encrypted, encryptedLen); // first two bytes means length
encryptor.Encrypt (rnd, secret, 48, encrypted + 2);
// send ClientKeyExchange
// 0x10 - handshake type "client key exchange"
SendHandshakeMsg (0x10, encrypted, encryptedLen + 2);
delete[] encrypted;
// send ChangeCipherSpecs
m_Site.write ((char *)changeCipherSpecs, sizeof (changeCipherSpecs));
// calculate master secret
uint8_t random[64];
memcpy (random, clientHello + 11, 32);
memcpy (random + 32, serverRandom, 32);
PRF (secret, "master secret", random, 64, 48, m_MasterSecret);
// create keys
memcpy (random, serverRandom, 32);
memcpy (random + 32, clientHello + 11, 32);
uint8_t keys[128]; // clientMACKey(32 or 20), serverMACKey(32 or 20), clientKey(32), serverKey(32)
PRF (m_MasterSecret, "key expansion", random, 64, 128, keys);
// create cipher
if (cipherSuite[1] == 0x3D)
{
LogPrint (eLogInfo, "Chiper suite is RSA_WITH_AES_256_CBC_SHA256");
m_Cipher = new TlsCipher_AES_256_CBC<CryptoPP::SHA256> (keys);
} }
else if (cipherSuite[1] == 0x35)
{
LogPrint (eLogInfo, "Chiper suite is RSA_WITH_AES_256_CBC_SHA");
m_Cipher = new TlsCipher_AES_256_CBC<CryptoPP::SHA1> (keys);
}
else else
{ LogPrint (eLogError, "Couldn't resolve address ", u.host_, ": ", ecode.message ());
// TODO: return "";
if (cipherSuite[1] == 0x05) }
LogPrint (eLogInfo, "Chiper suite is RSA_WITH_RC4_128_SHA");
m_Cipher = new TlsCipher_RC4_SHA (keys);
}
// send finished
SendFinishedMsg ();
// read ChangeCipherSpecs
uint8_t changeCipherSpecs1[6];
m_Site.read ((char *)changeCipherSpecs1, 6);
// read finished
m_Site.read ((char *)&type, 1);
m_Site.read ((char *)&version, 2);
m_Site.read ((char *)&length, 2);
length = be16toh (length);
char * finished1 = new char[length];
m_Site.read (finished1, length);
m_Cipher->Decrypt ((uint8_t *)finished1, length); // for streaming ciphers
delete[] finished1;
delete[] serverHello;
delete[] certificate;
delete[] serverHelloDone;
}
void TlsSession::SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len)
{
uint8_t handshakeHeader[9];
handshakeHeader[0] = 0x16; // handshake
handshakeHeader[1] = 0x03; handshakeHeader[2] = 0x03; // version is always TLS 1.2 (3,3)
htobe16buf (handshakeHeader + 3, len + 4); // length of payload
//payload starts
handshakeHeader[5] = handshakeType; // handshake type
handshakeHeader[6] = 0; // highest byte of payload length is always zero
htobe16buf (handshakeHeader + 7, len); // length of data
m_Site.write ((char *)handshakeHeader, 9);
m_FinishedHash.Update (handshakeHeader + 5, 4); // only payload counts
m_Site.write ((char *)data, len);
m_FinishedHash.Update (data, len);
}
void TlsSession::SendFinishedMsg ()
{
// 0x16 handshake
// 0x03, 0x03 version (TLS 1.2)
// 2 bytes length of handshake (80 or 64 bytes)
// handshake (encrypted)
// unencrypted context
// 0x14 handshake type (finished)
// 0x00, 0x00, 0x0C length of handshake payload
// 12 bytes of verified data
uint8_t finishedHashDigest[32], finishedPayload[40], encryptedPayload[80];
finishedPayload[0] = 0x14; // handshake type (finished)
finishedPayload[1] = 0; finishedPayload[2] = 0; finishedPayload[3] = 0x0C; // 12 bytes
m_FinishedHash.Final (finishedHashDigest);
PRF (m_MasterSecret, "client finished", finishedHashDigest, 32, 12, finishedPayload + 4);
uint8_t mac[32];
m_Cipher->CalculateMAC (0x16, finishedPayload, 16, mac);
size_t encryptedPayloadSize = m_Cipher->Encrypt (finishedPayload, 16, mac, encryptedPayload);
uint8_t finished[5];
finished[0] = 0x16; // handshake
finished[1] = 0x03; finished[2] = 0x03; // version is always TLS 1.2 (3,3)
htobe16buf (finished + 3, encryptedPayloadSize); // length of payload
m_Site.write ((char *)finished, sizeof (finished));
m_Site.write ((char *)encryptedPayload, encryptedPayloadSize);
}
void TlsSession::PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen,
size_t len, uint8_t * buf)
{
// secret is assumed 48 bytes
// random is not more than 64 bytes
CryptoPP::HMAC<CryptoPP::SHA256> hmac (secret, 48);
uint8_t seed[96]; size_t seedLen;
seedLen = strlen (label);
memcpy (seed, label, seedLen);
memcpy (seed + seedLen, random, randomLen);
seedLen += randomLen;
size_t offset = 0;
uint8_t a[128];
hmac.CalculateDigest (a, seed, seedLen);
while (offset < len)
{
memcpy (a + 32, seed, seedLen);
hmac.CalculateDigest (buf + offset, a, seedLen + 32);
offset += 32;
hmac.CalculateDigest (a, a, 32);
}
}
CryptoPP::RSA::PublicKey TlsSession::ExtractPublicKey (const uint8_t * certificate, size_t len)
{
CryptoPP::ByteQueue queue;
queue.Put (certificate, len);
queue.MessageEnd ();
// extract X.509
CryptoPP::BERSequenceDecoder x509Cert (queue);
CryptoPP::BERSequenceDecoder tbsCert (x509Cert);
// version
uint32_t ver;
CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED);
CryptoPP::BERDecodeUnsigned<uint32_t>(context, ver, CryptoPP::INTEGER);
// serial
CryptoPP::Integer serial;
serial.BERDecode(tbsCert);
// signature
CryptoPP::BERSequenceDecoder signature (tbsCert);
signature.SkipAll();
// issuer
CryptoPP::BERSequenceDecoder issuer (tbsCert);
issuer.SkipAll();
// validity
CryptoPP::BERSequenceDecoder validity (tbsCert);
validity.SkipAll();
// subject
CryptoPP::BERSequenceDecoder subject (tbsCert);
subject.SkipAll();
// public key
CryptoPP::BERSequenceDecoder publicKey (tbsCert);
CryptoPP::BERSequenceDecoder ident (publicKey);
ident.SkipAll ();
CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING);
key.Skip (1); // FIXME: probably bug in crypto++
CryptoPP::BERSequenceDecoder keyPair (key);
CryptoPP::Integer n, e;
n.BERDecode (keyPair);
e.BERDecode (keyPair);
CryptoPP::RSA::PublicKey ret;
ret.Initialize (n, e);
return ret;
}
void TlsSession::Send (const uint8_t * buf, size_t len)
{
uint8_t * out = new uint8_t[len + 64 + 5]; // 64 = 32 mac + 16 iv + upto 16 padding, 5 = header
out[0] = 0x17; // application data
out[1] = 0x03; out[2] = 0x03; // version
uint8_t mac[32];
m_Cipher->CalculateMAC (0x17, buf, len, mac);
size_t encryptedLen = m_Cipher->Encrypt (buf, len, mac, out + 5);
htobe16buf (out + 3, encryptedLen);
m_Site.write ((char *)out, encryptedLen + 5);
delete[] out;
}
bool TlsSession::Receive (std::ostream& rs)
{
if (m_Site.eof ()) return false;
uint8_t type; uint16_t version, length;
m_Site.read ((char *)&type, 1);
m_Site.read ((char *)&version, 2);
m_Site.read ((char *)&length, 2);
length = be16toh (length);
uint8_t * buf = new uint8_t[length];
m_Site.read ((char *)buf, length);
size_t decryptedLen = m_Cipher->Decrypt (buf, length);
rs.write ((char *)buf + m_Cipher->GetIVSize (), decryptedLen);
delete[] buf;
return true;
}
} }
} }

@ -5,11 +5,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <map> #include <map>
#include <cryptopp/osrng.h>
#include <cryptopp/rsa.h>
#include <boost/asio.hpp>
#include "Identity.h" #include "Identity.h"
#include "aes.h" #include "Crypto.h"
namespace i2p namespace i2p
{ {
@ -24,7 +21,6 @@ namespace data
Reseeder(); Reseeder();
~Reseeder(); ~Reseeder();
bool reseedNow(); // depreacted
int ReseedNowSU3 (); int ReseedNowSU3 ();
void LoadCertificates (); void LoadCertificates ();
@ -32,8 +28,7 @@ namespace data
private: private:
void LoadCertificate (const std::string& filename); void LoadCertificate (const std::string& filename);
std::string LoadCertificate (CryptoPP::ByteQueue& queue); // returns issuer's name
int ReseedFromSU3 (const std::string& host, bool https = false); int ReseedFromSU3 (const std::string& host, bool https = false);
int ProcessSU3File (const char * filename); int ProcessSU3File (const char * filename);
int ProcessSU3Stream (std::istream& s); int ProcessSU3Stream (std::istream& s);
@ -46,49 +41,6 @@ namespace data
std::map<std::string, PublicKey> m_SigningKeys; std::map<std::string, PublicKey> m_SigningKeys;
}; };
class TlsCipher
{
public:
virtual ~TlsCipher () {};
virtual void CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac) = 0;
virtual size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out) = 0;
virtual size_t Decrypt (uint8_t * buf, size_t len) = 0;
virtual size_t GetIVSize () const { return 0; }; // override for AES
};
class TlsSession
{
public:
TlsSession (const std::string& host, int port);
~TlsSession ();
void Send (const uint8_t * buf, size_t len);
bool Receive (std::ostream& rs);
bool IsEstablished () const { return m_IsEstablished; };
private:
void Handshake ();
void SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len);
void SendFinishedMsg ();
CryptoPP::RSA::PublicKey ExtractPublicKey (const uint8_t * certificate, size_t len);
void PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen,
size_t len, uint8_t * buf);
private:
bool m_IsEstablished;
boost::asio::ip::tcp::iostream m_Site;
CryptoPP::SHA256 m_FinishedHash;
uint8_t m_MasterSecret[64]; // actual size is 48, but must be multiple of 32
TlsCipher * m_Cipher;
};
} }
} }

@ -1,14 +1,13 @@
#include <fstream> #include <fstream>
#include <cryptopp/dh.h>
#include <cryptopp/dsa.h>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "CryptoConst.h" #include "Crypto.h"
#include "RouterContext.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "NetDb.h" #include "NetDb.h"
#include "util.h" #include "util.h"
#include "version.h" #include "version.h"
#include "Log.h"
#include "RouterContext.h"
namespace i2p namespace i2p
{ {
@ -22,6 +21,7 @@ namespace i2p
void RouterContext::Init () void RouterContext::Init ()
{ {
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000);
m_StartupTime = i2p::util::GetSecondsSinceEpoch (); m_StartupTime = i2p::util::GetSecondsSinceEpoch ();
if (!Load ()) if (!Load ())
CreateNewRouter (); CreateNewRouter ();
@ -41,7 +41,7 @@ namespace i2p
routerInfo.SetRouterIdentity (GetIdentity ()); routerInfo.SetRouterIdentity (GetIdentity ());
int port = i2p::util::config::GetArg("-port", 0); int port = i2p::util::config::GetArg("-port", 0);
if (!port) if (!port)
port = m_Rnd.GenerateWord32 (9111, 30777); // I2P network ports range port = rand () % (30777 - 9111) + 9111; // I2P network ports range
routerInfo.AddSSUAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port, routerInfo.GetIdentHash ()); routerInfo.AddSSUAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port, routerInfo.GetIdentHash ());
routerInfo.AddNTCPAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port); routerInfo.AddNTCPAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port);
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
@ -51,6 +51,7 @@ namespace i2p
routerInfo.SetProperty ("router.version", I2P_VERSION); routerInfo.SetProperty ("router.version", I2P_VERSION);
routerInfo.SetProperty ("stat_uptime", "90m"); routerInfo.SetProperty ("stat_uptime", "90m");
routerInfo.CreateBuffer (m_Keys); routerInfo.CreateBuffer (m_Keys);
m_RouterInfo.SetRouterIdentity (GetIdentity ());
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
} }
@ -61,6 +62,25 @@ namespace i2p
m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch (); m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
} }
void RouterContext::SetStatus (RouterStatus status)
{
if (status != m_Status)
{
m_Status = status;
switch (m_Status)
{
case eRouterStatusOK:
SetReachable ();
break;
case eRouterStatusFirewalled:
SetUnreachable ();
break;
default:
;
}
}
}
void RouterContext::UpdatePort (int port) void RouterContext::UpdatePort (int port)
{ {
bool updated = false; bool updated = false;
@ -92,16 +112,11 @@ namespace i2p
UpdateRouterInfo (); UpdateRouterInfo ();
} }
bool RouterContext::AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag) bool RouterContext::AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer)
{ {
bool ret = false; bool ret = m_RouterInfo.AddIntroducer (introducer);
auto address = routerInfo.GetSSUAddress (); if (ret)
if (address) UpdateRouterInfo ();
{
ret = m_RouterInfo.AddIntroducer (address, tag);
if (ret)
UpdateRouterInfo ();
}
return ret; return ret;
} }
@ -188,7 +203,7 @@ namespace i2p
{ {
if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU) if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU)
{ {
// insert NTCP address with host/port form SSU // insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addresses[i].host.to_string ().c_str (), addresses[i].port); m_RouterInfo.AddNTCPAddress (addresses[i].host.to_string ().c_str (), addresses[i].port);
break; break;
} }
@ -267,6 +282,7 @@ namespace i2p
m_Keys = keys; m_Keys = keys;
i2p::data::RouterInfo routerInfo(i2p::util::filesystem::GetFullPath (ROUTER_INFO)); // TODO i2p::data::RouterInfo routerInfo(i2p::util::filesystem::GetFullPath (ROUTER_INFO)); // TODO
m_RouterInfo.SetRouterIdentity (GetIdentity ());
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION); m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
m_RouterInfo.SetProperty ("router.version", I2P_VERSION); m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
@ -283,7 +299,7 @@ namespace i2p
i2p::data::Keys keys; i2p::data::Keys keys;
memcpy (keys.privateKey, m_Keys.GetPrivateKey (), sizeof (keys.privateKey)); memcpy (keys.privateKey, m_Keys.GetPrivateKey (), sizeof (keys.privateKey));
memcpy (keys.signingPrivateKey, m_Keys.GetSigningPrivateKey (), sizeof (keys.signingPrivateKey)); memcpy (keys.signingPrivateKey, m_Keys.GetSigningPrivateKey (), sizeof (keys.signingPrivateKey));
auto& ident = GetIdentity ().GetStandardIdentity (); auto& ident = GetIdentity ()->GetStandardIdentity ();
memcpy (keys.publicKey, ident.publicKey, sizeof (keys.publicKey)); memcpy (keys.publicKey, ident.publicKey, sizeof (keys.publicKey));
memcpy (keys.signingKey, ident.signingKey, sizeof (keys.signingKey)); memcpy (keys.signingKey, ident.signingKey, sizeof (keys.signingKey));
fk.write ((char *)&keys, sizeof (keys)); fk.write ((char *)&keys, sizeof (keys));

@ -6,8 +6,6 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <cryptopp/dsa.h>
#include <cryptopp/osrng.h>
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "Garlic.h" #include "Garlic.h"
@ -41,16 +39,15 @@ namespace i2p
return std::shared_ptr<const i2p::data::RouterInfo> (&m_RouterInfo, return std::shared_ptr<const i2p::data::RouterInfo> (&m_RouterInfo,
[](const i2p::data::RouterInfo *) {}); [](const i2p::data::RouterInfo *) {});
} }
CryptoPP::RandomNumberGenerator& GetRandomNumberGenerator () { return m_Rnd; };
uint32_t GetUptime () const; uint32_t GetUptime () const;
uint32_t GetStartupTime () const { return m_StartupTime; }; uint32_t GetStartupTime () const { return m_StartupTime; };
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; }; uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
RouterStatus GetStatus () const { return m_Status; }; RouterStatus GetStatus () const { return m_Status; };
void SetStatus (RouterStatus status) { m_Status = status; }; void SetStatus (RouterStatus status);
void UpdatePort (int port); // called from Daemon void UpdatePort (int port); // called from Daemon
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
bool AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag); bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
bool IsUnreachable () const; bool IsUnreachable () const;
void SetUnreachable (); void SetUnreachable ();
@ -69,7 +66,7 @@ namespace i2p
// implements LocalDestination // implements LocalDestination
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
const uint8_t * GetEncryptionPrivateKey () const { return m_Keys.GetPrivateKey (); }; const uint8_t * GetEncryptionPrivateKey () const { return m_Keys.GetPrivateKey (); };
const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ().GetStandardIdentity ().publicKey; }; const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ()->GetStandardIdentity ().publicKey; };
void SetLeaseSetUpdated () {}; void SetLeaseSetUpdated () {};
// implements GarlicDestination // implements GarlicDestination
@ -93,7 +90,6 @@ namespace i2p
i2p::data::RouterInfo m_RouterInfo; i2p::data::RouterInfo m_RouterInfo;
i2p::data::PrivateKeys m_Keys; i2p::data::PrivateKeys m_Keys;
CryptoPP::AutoSeededRandomPool m_Rnd;
uint64_t m_LastUpdateTime; uint64_t m_LastUpdateTime;
bool m_AcceptsTunnels, m_IsFloodfill; bool m_AcceptsTunnels, m_IsFloodfill;
uint64_t m_StartupTime; // in seconds since epoch uint64_t m_StartupTime; // in seconds since epoch

@ -3,15 +3,11 @@
#include "I2PEndian.h" #include "I2PEndian.h"
#include <fstream> #include <fstream>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <cryptopp/sha.h> #include "Crypto.h"
#include <cryptopp/dsa.h> #include "Base.h"
#include "CryptoConst.h"
#include "base64.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "Log.h" #include "Log.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "RouterContext.h"
namespace i2p namespace i2p
{ {
@ -41,21 +37,38 @@ namespace data
void RouterInfo::Update (const uint8_t * buf, int len) void RouterInfo::Update (const uint8_t * buf, int len)
{ {
if (!m_Buffer) // verify signature since we have indentity already
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; int l = len - m_RouterIdentity->GetSignatureLen ();
m_IsUpdated = true; if (m_RouterIdentity->Verify (buf, l, buf + l))
m_IsUnreachable = false; {
m_SupportedTransports = 0; // clean up
m_Caps = 0; m_IsUpdated = true;
m_Addresses.clear (); m_IsUnreachable = false;
m_Properties.clear (); m_SupportedTransports = 0;
memcpy (m_Buffer, buf, len); m_Caps = 0;
m_BufferLen = len; m_Addresses.clear ();
ReadFromBuffer (true); m_Properties.clear ();
// don't delete buffer until save to file // copy buffer
if (!m_Buffer)
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
// skip identity
size_t identityLen = m_RouterIdentity->GetFullLen ();
// read new RI
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
ReadFromStream (str);
// don't delete buffer until saved to the file
}
else
{
LogPrint (eLogError, "RouterInfo signature verification failed");
m_IsUnreachable = true;
}
m_RouterIdentity->DropVerifier ();
} }
void RouterInfo::SetRouterIdentity (const IdentityEx& identity) void RouterInfo::SetRouterIdentity (std::shared_ptr<const IdentityEx> identity)
{ {
m_RouterIdentity = identity; m_RouterIdentity = identity;
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
@ -94,19 +107,20 @@ namespace data
void RouterInfo::ReadFromBuffer (bool verifySignature) void RouterInfo::ReadFromBuffer (bool verifySignature)
{ {
size_t identityLen = m_RouterIdentity.FromBuffer (m_Buffer, m_BufferLen); m_RouterIdentity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
size_t identityLen = m_RouterIdentity->GetFullLen ();
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen)); std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
ReadFromStream (str); ReadFromStream (str);
if (verifySignature) if (verifySignature)
{ {
// verify signature // verify signature
int l = m_BufferLen - m_RouterIdentity.GetSignatureLen (); int l = m_BufferLen - m_RouterIdentity->GetSignatureLen ();
if (!m_RouterIdentity.Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l)) if (!m_RouterIdentity->Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l))
{ {
LogPrint (eLogError, "signature verification failed"); LogPrint (eLogError, "RouterInfo signature verification failed");
m_IsUnreachable = true; m_IsUnreachable = true;
} }
m_RouterIdentity.DropVerifier (); m_RouterIdentity->DropVerifier ();
} }
} }
@ -419,7 +433,7 @@ namespace data
if (!m_Buffer) if (!m_Buffer)
{ {
if (LoadFile ()) if (LoadFile ())
LogPrint ("Buffer for ", GetIdentHashAbbreviation (), " loaded from file"); LogPrint ("Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file");
} }
return m_Buffer; return m_Buffer;
} }
@ -429,7 +443,7 @@ namespace data
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp
std::stringstream s; std::stringstream s;
uint8_t ident[1024]; uint8_t ident[1024];
auto identLen = privateKeys.GetPublic ().ToBuffer (ident, 1024); auto identLen = privateKeys.GetPublic ()->ToBuffer (ident, 1024);
s.write ((char *)ident, identLen); s.write ((char *)ident, identLen);
WriteToStream (s); WriteToStream (s);
m_BufferLen = s.str ().size (); m_BufferLen = s.str ().size ();
@ -438,7 +452,7 @@ namespace data
memcpy (m_Buffer, s.str ().c_str (), m_BufferLen); memcpy (m_Buffer, s.str ().c_str (), m_BufferLen);
// signature // signature
privateKeys.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen); privateKeys.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen);
m_BufferLen += privateKeys.GetPublic ().GetSignatureLen (); m_BufferLen += privateKeys.GetPublic ()->GetSignatureLen ();
} }
void RouterInfo::SaveToFile (const std::string& fullPath) void RouterInfo::SaveToFile (const std::string& fullPath)
@ -481,6 +495,8 @@ namespace data
addr.cost = 2; addr.cost = 2;
addr.date = 0; addr.date = 0;
addr.mtu = 0; addr.mtu = 0;
for (auto it: m_Addresses) // don't insert same address twice
if (it == addr) return;
m_Addresses.push_back(addr); m_Addresses.push_back(addr);
m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eNTCPV4; m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eNTCPV4;
} }
@ -495,26 +511,23 @@ namespace data
addr.date = 0; addr.date = 0;
addr.mtu = mtu; addr.mtu = mtu;
memcpy (addr.key, key, 32); memcpy (addr.key, key, 32);
for (auto it: m_Addresses) // don't insert same address twice
if (it == addr) return;
m_Addresses.push_back(addr); m_Addresses.push_back(addr);
m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eSSUV4; m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eSSUV4;
m_Caps |= eSSUTesting; m_Caps |= eSSUTesting;
m_Caps |= eSSUIntroducer; m_Caps |= eSSUIntroducer;
} }
bool RouterInfo::AddIntroducer (const Address * address, uint32_t tag) bool RouterInfo::AddIntroducer (const Introducer& introducer)
{ {
for (auto& addr : m_Addresses) for (auto& addr : m_Addresses)
{ {
if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ()) if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ())
{ {
for (auto intro: addr.introducers) for (auto intro: addr.introducers)
if (intro.iTag == tag) return false; // already presented if (intro.iTag == introducer.iTag) return false; // already presented
Introducer x; addr.introducers.push_back (introducer);
x.iHost = address->host;
x.iPort = address->port;
x.iTag = tag;
memcpy (x.iKey, address->key, 32); // TODO: replace to Tag<32>
addr.introducers.push_back (x);
return true; return true;
} }
} }

@ -58,11 +58,12 @@ namespace data
eTransportSSU eTransportSSU
}; };
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
struct Introducer struct Introducer
{ {
boost::asio::ip::address iHost; boost::asio::ip::address iHost;
int iPort; int iPort;
Tag<32> iKey; IntroKey iKey;
uint32_t iTag; uint32_t iTag;
}; };
@ -75,7 +76,7 @@ namespace data
uint64_t date; uint64_t date;
uint8_t cost; uint8_t cost;
// SSU only // SSU only
Tag<32> key; // intro key for SSU IntroKey key; // intro key for SSU
std::vector<Introducer> introducers; std::vector<Introducer> introducers;
bool IsCompatible (const boost::asio::ip::address& other) const bool IsCompatible (const boost::asio::ip::address& other) const
@ -83,6 +84,16 @@ namespace data
return (host.is_v4 () && other.is_v4 ()) || return (host.is_v4 () && other.is_v4 ()) ||
(host.is_v6 () && other.is_v6 ()); (host.is_v6 () && other.is_v6 ());
} }
bool operator==(const Address& other) const
{
return transportStyle == other.transportStyle && host == other.host && port == other.port;
}
bool operator!=(const Address& other) const
{
return !(*this == other);
}
}; };
RouterInfo (const std::string& fullPath); RouterInfo (const std::string& fullPath);
@ -92,10 +103,9 @@ namespace data
RouterInfo (const uint8_t * buf, int len); RouterInfo (const uint8_t * buf, int len);
~RouterInfo (); ~RouterInfo ();
const IdentityEx& GetRouterIdentity () const { return m_RouterIdentity; }; std::shared_ptr<const IdentityEx> GetRouterIdentity () const { return m_RouterIdentity; };
void SetRouterIdentity (const IdentityEx& identity); void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity);
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); }; std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
std::string GetIdentHashAbbreviation () const { return GetIdentHash ().ToBase64 ().substr (0, 4); };
uint64_t GetTimestamp () const { return m_Timestamp; }; uint64_t GetTimestamp () const { return m_Timestamp; };
std::vector<Address>& GetAddresses () { return m_Addresses; }; std::vector<Address>& GetAddresses () { return m_Addresses; };
const Address * GetNTCPAddress (bool v4only = true) const; const Address * GetNTCPAddress (bool v4only = true) const;
@ -104,7 +114,7 @@ namespace data
void AddNTCPAddress (const char * host, int port); void AddNTCPAddress (const char * host, int port);
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0); void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
bool AddIntroducer (const Address * address, uint32_t tag); bool AddIntroducer (const Introducer& introducer);
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only
void DeleteProperty (const std::string& key); // called from RouterContext only void DeleteProperty (const std::string& key); // called from RouterContext only
@ -145,8 +155,8 @@ namespace data
void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; }; void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; };
// implements RoutingDestination // implements RoutingDestination
const IdentHash& GetIdentHash () const { return m_RouterIdentity.GetIdentHash (); }; const IdentHash& GetIdentHash () const { return m_RouterIdentity->GetIdentHash (); };
const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity.GetStandardIdentity ().publicKey; }; const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity->GetStandardIdentity ().publicKey; };
bool IsDestination () const { return false; }; bool IsDestination () const { return false; };
@ -166,7 +176,7 @@ namespace data
private: private:
std::string m_FullPath; std::string m_FullPath;
IdentityEx m_RouterIdentity; std::shared_ptr<const IdentityEx> m_RouterIdentity;
uint8_t * m_Buffer; uint8_t * m_Buffer;
int m_BufferLen; int m_BufferLen;
uint64_t m_Timestamp; uint64_t m_Timestamp;

@ -4,7 +4,7 @@
#include <stdlib.h> #include <stdlib.h>
#endif #endif
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "base64.h" #include "Base.h"
#include "Identity.h" #include "Identity.h"
#include "Log.h" #include "Log.h"
#include "Destination.h" #include "Destination.h"
@ -189,6 +189,8 @@ namespace client
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
Terminate (); Terminate ();
} }
else if (m_SocketType == eSAMSocketTypeStream)
HandleReceived (ecode, bytes_transferred);
else else
{ {
bytes_transferred += m_BufferOffset; bytes_transferred += m_BufferOffset;
@ -342,17 +344,17 @@ namespace client
m_Session = m_Owner.FindSession (id); m_Session = m_Owner.FindSession (id);
if (m_Session) if (m_Session)
{ {
i2p::data::IdentityEx dest; auto dest = std::make_shared<i2p::data::IdentityEx> ();
size_t len = dest.FromBase64(destination); size_t len = dest->FromBase64(destination);
if (len > 0) if (len > 0)
{ {
context.GetAddressBook().InsertAddress(dest); context.GetAddressBook().InsertAddress(dest);
auto leaseSet = m_Session->localDestination->FindLeaseSet(dest.GetIdentHash()); auto leaseSet = m_Session->localDestination->FindLeaseSet(dest->GetIdentHash());
if (leaseSet) if (leaseSet)
Connect(leaseSet); Connect(leaseSet);
else else
{ {
m_Session->localDestination->RequestDestination(dest.GetIdentHash(), m_Session->localDestination->RequestDestination(dest->GetIdentHash(),
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete, std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
shared_from_this(), std::placeholders::_1)); shared_from_this(), std::placeholders::_1));
} }
@ -451,7 +453,7 @@ namespace client
keys.GetPublic ().ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); keys.GetPublic ().ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
#else #else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
keys.GetPublic ().ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
#endif #endif
SendMessageReply (m_Buffer, len, false); SendMessageReply (m_Buffer, len, false);
} }
@ -462,11 +464,11 @@ namespace client
std::map<std::string, std::string> params; std::map<std::string, std::string> params;
ExtractParams (buf, params); ExtractParams (buf, params);
std::string& name = params[SAM_PARAM_NAME]; std::string& name = params[SAM_PARAM_NAME];
i2p::data::IdentityEx identity; std::shared_ptr<const i2p::data::IdentityEx> identity;
i2p::data::IdentHash ident; i2p::data::IdentHash ident;
if (name == "ME") if (name == "ME")
SendNamingLookupReply (m_Session->localDestination->GetIdentity ()); SendNamingLookupReply (m_Session->localDestination->GetIdentity ());
else if (context.GetAddressBook ().GetAddress (name, identity)) else if ((identity = context.GetAddressBook ().GetAddress (name)) != nullptr)
SendNamingLookupReply (identity); SendNamingLookupReply (identity);
else if (m_Session && m_Session->localDestination && else if (m_Session && m_Session->localDestination &&
context.GetAddressBook ().GetIdentHash (name, ident)) context.GetAddressBook ().GetIdentHash (name, ident))
@ -512,9 +514,9 @@ namespace client
} }
} }
void SAMSocket::SendNamingLookupReply (const i2p::data::IdentityEx& identity) void SAMSocket::SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity)
{ {
auto base64 = identity.ToBase64 (); auto base64 = identity->ToBase64 ();
#ifdef _MSC_VER #ifdef _MSC_VER
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ()); size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
#else #else
@ -631,7 +633,7 @@ namespace client
{ {
// send remote peer address // send remote peer address
uint8_t ident[1024]; uint8_t ident[1024];
size_t l = stream->GetRemoteIdentity ().ToBuffer (ident, 1024); size_t l = stream->GetRemoteIdentity ()->ToBuffer (ident, 1024);
size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE); size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
m_StreamBuffer[l1] = '\n'; m_StreamBuffer[l1] = '\n';
HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream

@ -111,7 +111,7 @@ namespace client
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote); void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote);
void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet); void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet);
void SendNamingLookupReply (const i2p::data::IdentityEx& identity); void SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity);
void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident); void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident);
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode); void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
void SendSessionCreateReplyOk (); void SendSessionCreateReplyOk ();

@ -283,7 +283,11 @@ namespace transport
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
auto it = m_Sessions.find (remoteEndpoint); auto it = m_Sessions.find (remoteEndpoint);
if (it != m_Sessions.end ()) if (it != m_Sessions.end ())
{
session = it->second; session = it->second;
if (peerTest && session->GetState () == eSessionStateEstablished)
session->SendPeerTest ();
}
else else
{ {
// otherwise create new session // otherwise create new session
@ -295,7 +299,7 @@ namespace transport
if (!router->UsesIntroducer ()) if (!router->UsesIntroducer ())
{ {
// connect directly // connect directly
LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ", LogPrint ("Creating new SSU session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ()); remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
session->Connect (); session->Connect ();
} }
@ -331,7 +335,7 @@ namespace transport
m_Sessions[introducerEndpoint] = introducerSession; m_Sessions[introducerEndpoint] = introducerSession;
} }
// introduce // introduce
LogPrint ("Introduce new SSU session to [", router->GetIdentHashAbbreviation (), LogPrint ("Introduce new SSU session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
"] through introducer ", introducer->iHost, ":", introducer->iPort); "] through introducer ", introducer->iHost, ":", introducer->iPort);
session->WaitForIntroduction (); session->WaitForIntroduction ();
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
@ -339,7 +343,7 @@ namespace transport
uint8_t buf[1]; uint8_t buf[1];
Send (buf, 0, remoteEndpoint); // send HolePunch Send (buf, 0, remoteEndpoint); // send HolePunch
} }
introducerSession->Introduce (introducer->iTag, introducer->iKey); introducerSession->Introduce (*introducer);
} }
else else
{ {
@ -352,7 +356,7 @@ namespace transport
} }
} }
else else
LogPrint (eLogWarning, "Router ", router->GetIdentHashAbbreviation (), " doesn't have SSU address"); LogPrint (eLogWarning, "Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
} }
return session; return session;
} }
@ -383,7 +387,7 @@ namespace transport
if (filter (s.second)) filteredSessions.push_back (s.second); if (filter (s.second)) filteredSessions.push_back (s.second);
if (filteredSessions.size () > 0) if (filteredSessions.size () > 0)
{ {
auto ind = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, filteredSessions.size ()-1); auto ind = rand () % filteredSessions.size ();
return filteredSessions[ind]; return filteredSessions[ind];
} }
return nullptr; return nullptr;
@ -468,10 +472,15 @@ namespace transport
{ {
for (auto it1: introducers) for (auto it1: introducers)
{ {
auto router = it1->GetRemoteRouter (); auto& ep = it1->GetRemoteEndpoint ();
if (router && i2p::context.AddIntroducer (*router, it1->GetRelayTag ())) i2p::data::RouterInfo::Introducer introducer;
introducer.iHost = ep.address ();
introducer.iPort = ep.port ();
introducer.iTag = it1->GetRelayTag ();
introducer.iKey = it1->GetIntroKey ();
if (i2p::context.AddIntroducer (introducer))
{ {
newList.push_back (it1->GetRemoteEndpoint ()); newList.push_back (ep);
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break; if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
} }
} }

@ -9,7 +9,7 @@
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "aes.h" #include "Crypto.h"
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"

@ -26,13 +26,10 @@ namespace transport
SSUData::SSUData (SSUSession& session): SSUData::SSUData (SSUSession& session):
m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()), m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()),
m_IncompleteMessagesCleanupTimer (session.GetService ()) m_IncompleteMessagesCleanupTimer (session.GetService ()),
m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
m_PacketSize (m_MaxPacketSize)
{ {
m_MaxPacketSize = session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE;
m_PacketSize = m_MaxPacketSize;
auto remoteRouter = session.GetRemoteRouter ();
if (remoteRouter)
AdjustPacketSize (*remoteRouter);
} }
SSUData::~SSUData () SSUData::~SSUData ()
@ -51,9 +48,10 @@ namespace transport
m_IncompleteMessagesCleanupTimer.cancel (); m_IncompleteMessagesCleanupTimer.cancel ();
} }
void SSUData::AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter) void SSUData::AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter)
{ {
auto ssuAddress = remoteRouter.GetSSUAddress (); if (remoteRouter) return;
auto ssuAddress = remoteRouter->GetSSUAddress ();
if (ssuAddress && ssuAddress->mtu) if (ssuAddress && ssuAddress->mtu)
{ {
if (m_Session.IsV6 ()) if (m_Session.IsV6 ())
@ -80,7 +78,7 @@ namespace transport
{ {
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent); auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
if (routerInfo) if (routerInfo)
AdjustPacketSize (*routerInfo); AdjustPacketSize (routerInfo);
} }
void SSUData::ProcessSentMessageAck (uint32_t msgID) void SSUData::ProcessSentMessageAck (uint32_t msgID)
@ -372,7 +370,7 @@ namespace transport
payload++; payload++;
*payload = 1; // number of ACKs *payload = 1; // number of ACKs
payload++; payload++;
*(uint32_t *)(payload) = htobe32 (msgID); // msgID htobe32buf (payload, msgID); // msgID
payload += 4; payload += 4;
*payload = 0; // number of fragments *payload = 0; // number of fragments

@ -80,7 +80,7 @@ namespace transport
{ {
public: public:
SSUData (SSUSession& session); SSUData (SSUSession& session);
~SSUData (); ~SSUData ();
void Start (); void Start ();
@ -90,6 +90,7 @@ namespace transport
void FlushReceivedMessage (); void FlushReceivedMessage ();
void Send (std::shared_ptr<i2p::I2NPMessage> msg); void Send (std::shared_ptr<i2p::I2NPMessage> msg);
void AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter);
void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent); void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
private: private:
@ -109,7 +110,6 @@ namespace transport
void ScheduleIncompleteMessagesCleanup (); void ScheduleIncompleteMessagesCleanup ();
void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode); void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode);
void AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter);
private: private:

@ -1,7 +1,7 @@
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <cryptopp/dh.h> #include <openssl/dh.h>
#include <cryptopp/sha.h> #include <openssl/sha.h>
#include "CryptoConst.h" #include <openssl/rand.h>
#include "Log.h" #include "Log.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "RouterContext.h" #include "RouterContext.h"
@ -16,9 +16,22 @@ namespace transport
SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ): TransportSession (router), std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ): TransportSession (router),
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_Timer (GetService ()), m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_Timer (GetService ()),
m_PeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false), m_IsPeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false),
m_RelayTag (0),m_Data (*this), m_IsDataReceived (false) m_RelayTag (0),m_Data (*this), m_IsDataReceived (false)
{ {
if (router)
{
// we are client
auto address = router->GetSSUAddress ();
if (address) m_IntroKey = address->key;
m_Data.AdjustPacketSize (router); // mtu
}
else
{
// we are server
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (address) m_IntroKey = address->key;
}
m_CreationTime = i2p::util::GetSecondsSinceEpoch (); m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
} }
@ -33,13 +46,8 @@ namespace transport
void SSUSession::CreateAESandMacKey (const uint8_t * pubKey) void SSUSession::CreateAESandMacKey (const uint8_t * pubKey)
{ {
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
uint8_t sharedKey[256]; uint8_t sharedKey[256];
if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey)) m_DHKeysPair->Agree (pubKey, sharedKey);
{
LogPrint (eLogError, "Couldn't create shared key");
return;
};
uint8_t * sessionKey = m_SessionKey, * macKey = m_MacKey; uint8_t * sessionKey = m_SessionKey, * macKey = m_MacKey;
if (sharedKey[0] & 0x80) if (sharedKey[0] & 0x80)
@ -68,7 +76,7 @@ namespace transport
} }
memcpy (sessionKey, nonZero, 32); memcpy (sessionKey, nonZero, 32);
CryptoPP::SHA256().CalculateDigest(macKey, nonZero, 64 - (nonZero - sharedKey)); SHA256(nonZero, 64 - (nonZero - sharedKey), macKey);
} }
m_IsSessionKey = true; m_IsSessionKey = true;
m_SessionKeyEncryption.SetKey (m_SessionKey); m_SessionKeyEncryption.SetKey (m_SessionKey);
@ -97,9 +105,8 @@ namespace transport
else else
{ {
// try intro key depending on side // try intro key depending on side
auto introKey = GetIntroKey (); if (Validate (buf, len, m_IntroKey))
if (introKey && Validate (buf, len, introKey)) Decrypt (buf, len, m_IntroKey);
Decrypt (buf, len, introKey);
else else
{ {
// try own intro key // try own intro key
@ -184,7 +191,7 @@ namespace transport
void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len) void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len)
{ {
if (!m_RemoteRouter || !m_DHKeysPair) if (!IsOutgoing () || !m_DHKeysPair)
{ {
LogPrint (eLogWarning, "Unsolicited session created message"); LogPrint (eLogWarning, "Unsolicited session created message");
return; return;
@ -196,7 +203,7 @@ namespace transport
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
uint8_t * y = payload; uint8_t * y = payload;
CreateAESandMacKey (y); CreateAESandMacKey (y);
s.Insert (m_DHKeysPair->publicKey, 256); // x s.Insert (m_DHKeysPair->GetPublicKey (), 256); // x
s.Insert (y, 256); // y s.Insert (y, 256); // y
payload += 256; payload += 256;
uint8_t addressSize = *payload; uint8_t addressSize = *payload;
@ -232,7 +239,7 @@ namespace transport
payload += 4; // relayTag payload += 4; // relayTag
payload += 4; // signed on time payload += 4; // signed on time
// decrypt signature // decrypt signature
size_t signatureLen = m_RemoteIdentity.GetSignatureLen (); size_t signatureLen = m_RemoteIdentity->GetSignatureLen ();
size_t paddingSize = signatureLen & 0x0F; // %16 size_t paddingSize = signatureLen & 0x0F; // %16
if (paddingSize > 0) signatureLen += (16 - paddingSize); if (paddingSize > 0) signatureLen += (16 - paddingSize);
//TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved
@ -240,8 +247,7 @@ namespace transport
m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload); m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload);
// verify // verify
if (!s.Verify (m_RemoteIdentity, payload)) if (!s.Verify (m_RemoteIdentity, payload))
LogPrint (eLogError, "SSU signature verification failed"); LogPrint (eLogError, "Session created SSU signature verification failed");
m_RemoteIdentity.DropVerifier ();
SendSessionConfirmed (y, ourAddress, addressSize + 2); SendSessionConfirmed (y, ourAddress, addressSize + 2);
} }
@ -253,31 +259,28 @@ namespace transport
payload++; // identity fragment info payload++; // identity fragment info
uint16_t identitySize = bufbe16toh (payload); uint16_t identitySize = bufbe16toh (payload);
payload += 2; // size of identity fragment payload += 2; // size of identity fragment
m_RemoteIdentity.FromBuffer (payload, identitySize); SetRemoteIdentity (std::make_shared<i2p::data::IdentityEx> (payload, identitySize));
m_Data.UpdatePacketSize (m_RemoteIdentity.GetIdentHash ()); m_Data.UpdatePacketSize (m_RemoteIdentity->GetIdentHash ());
payload += identitySize; // identity payload += identitySize; // identity
if (m_SignedData)
m_SignedData->Insert (payload, 4); // insert Alice's signed on time
payload += 4; // signed-on time payload += 4; // signed-on time
size_t paddingSize = (payload - buf) + m_RemoteIdentity.GetSignatureLen (); size_t paddingSize = (payload - buf) + m_RemoteIdentity->GetSignatureLen ();
paddingSize &= 0x0F; // %16 paddingSize &= 0x0F; // %16
if (paddingSize > 0) paddingSize = 16 - paddingSize; if (paddingSize > 0) paddingSize = 16 - paddingSize;
payload += paddingSize; payload += paddingSize;
// TODO: verify signature (need data from session request), payload points to signature // verify
if (m_SignedData && !m_SignedData->Verify (m_RemoteIdentity, payload))
LogPrint (eLogError, "Session confirmed SSU signature verification failed");
m_Data.Send (CreateDeliveryStatusMsg (0)); m_Data.Send (CreateDeliveryStatusMsg (0));
Established (); Established ();
} }
void SSUSession::SendSessionRequest () void SSUSession::SendSessionRequest ()
{ {
auto introKey = GetIntroKey ();
if (!introKey)
{
LogPrint (eLogError, "SSU is not supported");
return;
}
uint8_t buf[320 + 18]; // 304 bytes for ipv4, 320 for ipv6 uint8_t buf[320 + 18]; // 304 bytes for ipv4, 320 for ipv6
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
memcpy (payload, m_DHKeysPair->publicKey, 256); // x memcpy (payload, m_DHKeysPair->GetPublicKey (), 256); // x
bool isV4 = m_RemoteEndpoint.address ().is_v4 (); bool isV4 = m_RemoteEndpoint.address ().is_v4 ();
if (isV4) if (isV4)
{ {
@ -291,13 +294,12 @@ namespace transport
} }
uint8_t iv[16]; uint8_t iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); RAND_bytes (iv, 16); // random iv
rnd.GenerateBlock (iv, 16); // random iv FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, isV4 ? 304 : 320, m_IntroKey, iv, m_IntroKey);
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, isV4 ? 304 : 320, introKey, iv, introKey);
m_Server.Send (buf, isV4 ? 304 : 320, m_RemoteEndpoint); m_Server.Send (buf, isV4 ? 304 : 320, m_RemoteEndpoint);
} }
void SSUSession::SendRelayRequest (uint32_t iTag, const uint8_t * iKey) void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer)
{ {
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (!address) if (!address)
@ -308,7 +310,7 @@ namespace transport
uint8_t buf[96 + 18]; uint8_t buf[96 + 18];
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
htobe32buf (payload, iTag); htobe32buf (payload, introducer.iTag);
payload += 4; payload += 4;
*payload = 0; // no address *payload = 0; // no address
payload++; payload++;
@ -318,35 +320,32 @@ namespace transport
payload++; payload++;
memcpy (payload, (const uint8_t *)address->key, 32); memcpy (payload, (const uint8_t *)address->key, 32);
payload += 32; payload += 32;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); RAND_bytes (payload, 4); // nonce
htobe32buf (payload, rnd.GenerateWord32 ()); // nonce
uint8_t iv[16]; uint8_t iv[16];
rnd.GenerateBlock (iv, 16); // random iv RAND_bytes (iv, 16); // random iv
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, introducer.iKey, iv, introducer.iKey);
m_Server.Send (buf, 96, m_RemoteEndpoint); m_Server.Send (buf, 96, m_RemoteEndpoint);
} }
void SSUSession::SendSessionCreated (const uint8_t * x) void SSUSession::SendSessionCreated (const uint8_t * x)
{ {
auto introKey = GetIntroKey ();
auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () :
i2p::context.GetRouterInfo ().GetSSUAddress (true); //v4 only i2p::context.GetRouterInfo ().GetSSUAddress (true); //v4 only
if (!introKey || !address) if (!address)
{ {
LogPrint (eLogError, "SSU is not supported"); LogPrint (eLogError, "SSU is not supported");
return; return;
} }
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
SignedData s; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time SignedData s; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time
s.Insert (x, 256); // x s.Insert (x, 256); // x
uint8_t buf[384 + 18]; uint8_t buf[384 + 18];
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
memcpy (payload, m_DHKeysPair->publicKey, 256); memcpy (payload, m_DHKeysPair->GetPublicKey (), 256);
s.Insert (payload, 256); // y s.Insert (payload, 256); // y
payload += 256; payload += 256;
if (m_RemoteEndpoint.address ().is_v4 ()) if (m_RemoteEndpoint.address ().is_v4 ())
@ -378,7 +377,7 @@ namespace transport
uint32_t relayTag = 0; uint32_t relayTag = 0;
if (i2p::context.GetRouterInfo ().IsIntroducer ()) if (i2p::context.GetRouterInfo ().IsIntroducer ())
{ {
relayTag = rnd.GenerateWord32 (); RAND_bytes((uint8_t *)&relayTag, 4);
if (!relayTag) relayTag = 1; if (!relayTag) relayTag = 1;
m_Server.AddRelay (relayTag, m_RemoteEndpoint); m_Server.AddRelay (relayTag, m_RemoteEndpoint);
} }
@ -386,14 +385,18 @@ namespace transport
payload += 4; // relay tag payload += 4; // relay tag
htobe32buf (payload, i2p::util::GetSecondsSinceEpoch ()); // signed on time htobe32buf (payload, i2p::util::GetSecondsSinceEpoch ()); // signed on time
payload += 4; payload += 4;
s.Insert (payload - 8, 8); // relayTag and signed on time s.Insert (payload - 8, 4); // relayTag
// we have to store this signed data for session confirmed
// same data but signed on time, it will Alice's there
m_SignedData = std::unique_ptr<SignedData>(new SignedData (s));
s.Insert (payload - 4, 4); // BOB's signed on time
s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature
// TODO: fill padding with random data // TODO: fill padding with random data
uint8_t iv[16]; uint8_t iv[16];
rnd.GenerateBlock (iv, 16); // random iv RAND_bytes (iv, 16); // random iv
// encrypt signature and padding with newly created session key // encrypt signature and padding with newly created session key
size_t signatureLen = i2p::context.GetIdentity ().GetSignatureLen (); size_t signatureLen = i2p::context.GetIdentity ()->GetSignatureLen ();
size_t paddingSize = signatureLen & 0x0F; // %16 size_t paddingSize = signatureLen & 0x0F; // %16
if (paddingSize > 0) signatureLen += (16 - paddingSize); if (paddingSize > 0) signatureLen += (16 - paddingSize);
m_SessionKeyEncryption.SetIV (iv); m_SessionKeyEncryption.SetIV (iv);
@ -402,7 +405,7 @@ namespace transport
size_t msgLen = payload - buf; size_t msgLen = payload - buf;
// encrypt message with intro key // encrypt message with intro key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, msgLen, introKey, iv, introKey); FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, msgLen, m_IntroKey, iv, m_IntroKey);
Send (buf, msgLen); Send (buf, msgLen);
} }
@ -412,15 +415,15 @@ namespace transport
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
*payload = 1; // 1 fragment *payload = 1; // 1 fragment
payload++; // info payload++; // info
size_t identLen = i2p::context.GetIdentity ().GetFullLen (); // 387+ bytes size_t identLen = i2p::context.GetIdentity ()->GetFullLen (); // 387+ bytes
htobe16buf (payload, identLen); htobe16buf (payload, identLen);
payload += 2; // cursize payload += 2; // cursize
i2p::context.GetIdentity ().ToBuffer (payload, identLen); i2p::context.GetIdentity ()->ToBuffer (payload, identLen);
payload += identLen; payload += identLen;
uint32_t signedOnTime = i2p::util::GetSecondsSinceEpoch (); uint32_t signedOnTime = i2p::util::GetSecondsSinceEpoch ();
htobe32buf (payload, signedOnTime); // signed on time htobe32buf (payload, signedOnTime); // signed on time
payload += 4; payload += 4;
auto signatureLen = i2p::context.GetIdentity ().GetSignatureLen (); auto signatureLen = i2p::context.GetIdentity ()->GetSignatureLen ();
size_t paddingSize = ((payload - buf) + signatureLen)%16; size_t paddingSize = ((payload - buf) + signatureLen)%16;
if (paddingSize > 0) paddingSize = 16 - paddingSize; if (paddingSize > 0) paddingSize = 16 - paddingSize;
// TODO: fill padding // TODO: fill padding
@ -428,7 +431,7 @@ namespace transport
// signature // signature
SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time
s.Insert (m_DHKeysPair->publicKey, 256); // x s.Insert (m_DHKeysPair->GetPublicKey (), 256); // x
s.Insert (y, 256); // y s.Insert (y, 256); // y
s.Insert (ourAddress, ourAddressLen); // our address/port as seem by party s.Insert (ourAddress, ourAddressLen); // our address/port as seem by party
if (m_RemoteEndpoint.address ().is_v4 ()) if (m_RemoteEndpoint.address ().is_v4 ())
@ -443,8 +446,7 @@ namespace transport
size_t msgLen = payload - buf; size_t msgLen = payload - buf;
uint8_t iv[16]; uint8_t iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); RAND_bytes (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, msgLen, m_SessionKey, iv, m_MacKey); FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CONFIRMED, buf, msgLen, m_SessionKey, iv, m_MacKey);
Send (buf, msgLen); Send (buf, msgLen);
@ -519,8 +521,7 @@ namespace transport
{ {
// ecrypt with Alice's intro key // ecrypt with Alice's intro key
uint8_t iv[16]; uint8_t iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); RAND_bytes (iv, 16); // random iv
rnd.GenerateBlock (iv, 16); // random iv
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80, introKey, iv, introKey); FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80, introKey, iv, introKey);
m_Server.Send (buf, isV4 ? 64 : 80, from); m_Server.Send (buf, isV4 ? 64 : 80, from);
} }
@ -546,8 +547,7 @@ namespace transport
payload += 2; // port payload += 2; // port
*payload = 0; // challenge size *payload = 0; // challenge size
uint8_t iv[16]; uint8_t iv[16];
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); RAND_bytes (iv, 16); // random iv
rnd.GenerateBlock (iv, 16); // random iv
FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, 48, session->m_SessionKey, iv, session->m_MacKey); FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, 48, session->m_SessionKey, iv, session->m_MacKey);
m_Server.Send (buf, 48, session->m_RemoteEndpoint); m_Server.Send (buf, 48, session->m_RemoteEndpoint);
LogPrint (eLogDebug, "SSU relay intro sent"); LogPrint (eLogDebug, "SSU relay intro sent");
@ -602,7 +602,7 @@ namespace transport
} }
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len,
const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey) const i2p::crypto::AESKey& aesKey, const uint8_t * iv, const i2p::crypto::MACKey& macKey)
{ {
if (len < sizeof (SSUHeader)) if (len < sizeof (SSUHeader))
{ {
@ -635,7 +635,7 @@ namespace transport
} }
//TODO: we are using a dirty solution here but should work for now //TODO: we are using a dirty solution here but should work for now
SSUHeader * header = (SSUHeader *)buf; SSUHeader * header = (SSUHeader *)buf;
i2p::context.GetRandomNumberGenerator ().GenerateBlock (header->iv, 16); // random iv RAND_bytes (header->iv, 16); // random iv
m_SessionKeyEncryption.SetIV (header->iv); m_SessionKeyEncryption.SetIV (header->iv);
header->flag = payloadType << 4; // MSB is 0 header->flag = payloadType << 4; // MSB is 0
htobe32buf (&(header->time), i2p::util::GetSecondsSinceEpoch ()); htobe32buf (&(header->time), i2p::util::GetSecondsSinceEpoch ());
@ -648,7 +648,7 @@ namespace transport
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac); 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 i2p::crypto::AESKey& aesKey)
{ {
if (len < sizeof (SSUHeader)) if (len < sizeof (SSUHeader))
{ {
@ -683,7 +683,7 @@ namespace transport
} }
} }
bool SSUSession::Validate (uint8_t * buf, size_t len, const uint8_t * macKey) bool SSUSession::Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey)
{ {
if (len < sizeof (SSUHeader)) if (len < sizeof (SSUHeader))
{ {
@ -715,7 +715,7 @@ namespace transport
void SSUSession::WaitForConnect () void SSUSession::WaitForConnect ()
{ {
if (!m_RemoteRouter) // incoming session if (!IsOutgoing ()) // incoming session
ScheduleConnectTimer (); ScheduleConnectTimer ();
else else
LogPrint (eLogError, "SSU wait for connect for outgoing session"); LogPrint (eLogError, "SSU wait for connect for outgoing session");
@ -739,7 +739,7 @@ namespace transport
} }
} }
void SSUSession::Introduce (uint32_t iTag, const uint8_t * iKey) void SSUSession::Introduce (const i2p::data::RouterInfo::Introducer& introducer)
{ {
if (m_State == eSessionStateUnknown) if (m_State == eSessionStateUnknown)
{ {
@ -748,7 +748,7 @@ namespace transport
m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer, m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
} }
SendRelayRequest (iTag, iKey); SendRelayRequest (introducer);
} }
void SSUSession::WaitForIntroduction () void SSUSession::WaitForIntroduction ()
@ -777,15 +777,12 @@ namespace transport
void SSUSession::Established () void SSUSession::Established ()
{ {
m_State = eSessionStateEstablished; m_State = eSessionStateEstablished;
if (m_DHKeysPair) m_DHKeysPair = nullptr;
{ m_SignedData = nullptr;
delete m_DHKeysPair;
m_DHKeysPair = nullptr;
}
m_Data.Start (); m_Data.Start ();
m_Data.Send (ToSharedI2NPMessage(CreateDatabaseStoreMsg ())); m_Data.Send (CreateDatabaseStoreMsg ());
transports.PeerConnected (shared_from_this ()); transports.PeerConnected (shared_from_this ());
if (m_PeerTest && (m_RemoteRouter && m_RemoteRouter->IsPeerTesting ())) if (m_IsPeerTest)
SendPeerTest (); SendPeerTest ();
ScheduleTermination (); ScheduleTermination ();
} }
@ -816,21 +813,6 @@ namespace transport
} }
} }
const uint8_t * SSUSession::GetIntroKey () const
{
if (m_RemoteRouter)
{
// we are client
auto address = m_RemoteRouter->GetSSUAddress ();
return address ? (const uint8_t *)address->key : nullptr;
}
else
{
// we are server
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
return address ? (const uint8_t *)address->key : nullptr;
}
}
void SSUSession::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) void SSUSession::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
{ {
@ -994,8 +976,7 @@ namespace transport
memcpy (payload, introKey, 32); // intro key memcpy (payload, introKey, 32); // intro key
// send // send
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); RAND_bytes (iv, 16); // random iv
rnd.GenerateBlock (iv, 16); // random iv
if (toAddress) if (toAddress)
{ {
// encrypt message with specified intro key // encrypt message with specified intro key
@ -1021,9 +1002,10 @@ namespace transport
LogPrint (eLogError, "SSU is not supported. Can't send peer test"); LogPrint (eLogError, "SSU is not supported. Can't send peer test");
return; return;
} }
uint32_t nonce = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); uint32_t nonce;
RAND_bytes ((uint8_t *)&nonce, 4);
if (!nonce) nonce = 1; if (!nonce) nonce = 1;
m_PeerTest = false; m_IsPeerTest = false;
m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1); m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1);
SendPeerTest (nonce, 0, 0, address->key, false, false); // address and port always zero for Alice SendPeerTest (nonce, 0, 0, address->key, false, false); // address and port always zero for Alice
} }

@ -4,8 +4,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <set> #include <set>
#include <memory> #include <memory>
#include "aes.h" #include "Crypto.h"
#include "hmac.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "TransportSession.h" #include "TransportSession.h"
#include "SSUData.h" #include "SSUData.h"
@ -70,7 +69,7 @@ namespace transport
void Connect (); void Connect ();
void WaitForConnect (); void WaitForConnect ();
void Introduce (uint32_t iTag, const uint8_t * iKey); void Introduce (const i2p::data::RouterInfo::Introducer& introducer);
void WaitForIntroduction (); void WaitForIntroduction ();
void Close (); void Close ();
void Done (); void Done ();
@ -85,6 +84,7 @@ namespace transport
void SendKeepAlive (); void SendKeepAlive ();
uint32_t GetRelayTag () const { return m_RelayTag; }; uint32_t GetRelayTag () const { return m_RelayTag; };
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
uint32_t GetCreationTime () const { return m_CreationTime; }; uint32_t GetCreationTime () const { return m_CreationTime; };
void FlushData (); void FlushData ();
@ -98,7 +98,7 @@ namespace transport
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);
void SendSessionRequest (); void SendSessionRequest ();
void SendRelayRequest (uint32_t iTag, const uint8_t * iKey); void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer);
void ProcessSessionCreated (uint8_t * buf, size_t len); void ProcessSessionCreated (uint8_t * buf, size_t len);
void SendSessionCreated (const uint8_t * x); void SendSessionCreated (const uint8_t * x);
void ProcessSessionConfirmed (uint8_t * buf, size_t len); void ProcessSessionConfirmed (uint8_t * buf, size_t len);
@ -120,12 +120,11 @@ namespace transport
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 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 i2p::crypto::AESKey& aesKey, const uint8_t * iv, const i2p::crypto::MACKey& macKey);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key 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 Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey);
void DecryptSessionKey (uint8_t * buf, size_t len); 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 i2p::crypto::MACKey& macKey);
const uint8_t * GetIntroKey () const;
void ScheduleTermination (); void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode); void HandleTerminationTimer (const boost::system::error_code& ecode);
@ -136,7 +135,7 @@ namespace transport
SSUServer& m_Server; SSUServer& m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint; boost::asio::ip::udp::endpoint m_RemoteEndpoint;
boost::asio::deadline_timer m_Timer; boost::asio::deadline_timer m_Timer;
bool m_PeerTest; bool m_IsPeerTest;
SessionState m_State; SessionState m_State;
bool m_IsSessionKey; bool m_IsSessionKey;
uint32_t m_RelayTag; uint32_t m_RelayTag;
@ -144,9 +143,11 @@ namespace transport
i2p::crypto::CBCDecryption m_SessionKeyDecryption; i2p::crypto::CBCDecryption m_SessionKeyDecryption;
i2p::crypto::AESKey m_SessionKey; i2p::crypto::AESKey m_SessionKey;
i2p::crypto::MACKey m_MacKey; i2p::crypto::MACKey m_MacKey;
i2p::data::RouterInfo::IntroKey m_IntroKey;
uint32_t m_CreationTime; // seconds since epoch uint32_t m_CreationTime; // seconds since epoch
SSUData m_Data; SSUData m_Data;
bool m_IsDataReceived; bool m_IsDataReceived;
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only
}; };

@ -1,6 +1,4 @@
#include <memory> #include <memory>
#include <cryptopp/integer.h>
#include <cryptopp/eccrypto.h>
#include "Log.h" #include "Log.h"
#include "Signature.h" #include "Signature.h"
@ -14,82 +12,360 @@ namespace crypto
Ed25519 () Ed25519 ()
{ {
q = CryptoPP::Integer::Power2 (255) - CryptoPP::Integer (19); // 2^255-19 BN_CTX * ctx = BN_CTX_new ();
l = CryptoPP::Integer::Power2 (252) + CryptoPP::Integer ("27742317777372353535851937790883648493"); BIGNUM * two = BN_new (), * tmp = BN_new ();
BN_set_word (two, 2);
q = BN_new ();
// 2^255-19
BN_set_word (tmp, 255);
BN_exp (q, two, tmp, ctx);
BN_sub_word (q, 19);
// q_2 = q-2
q_2 = BN_dup (q);
BN_sub_word (q_2, 2);
l = BN_new ();
// 2^252 + 27742317777372353535851937790883648493 // 2^252 + 27742317777372353535851937790883648493
d = CryptoPP::Integer (-121665) * CryptoPP::Integer (121666).InverseMod (q); // -121665/121666 BN_set_word (tmp, 252);
I = a_exp_b_mod_c (CryptoPP::Integer::Two (), (q - CryptoPP::Integer::One ()).DividedBy (4), q); BN_exp (l, two, tmp, ctx);
B = DecodePoint (CryptoPP::Integer (4)*CryptoPP::Integer (5).InverseMod (q)); two_252_2 = BN_dup (l);
BN_dec2bn (&tmp, "27742317777372353535851937790883648493");
BN_add (l, l, tmp);
BN_sub_word (two_252_2, 2); // 2^252 - 2
// -121665*inv(121666)
d = BN_new ();
BN_set_word (tmp, 121666);
Inv (tmp, ctx);
BN_set_word (d, 121665);
BN_set_negative (d, -1);
BN_mul (d, d, tmp, ctx);
// 2^((q-1)/4)
I = BN_new ();
BN_free (tmp);
tmp = BN_dup (q);
BN_sub_word (tmp, 1);
BN_div_word (tmp, 4);
BN_mod_exp (I, two, tmp, q, ctx);
// 4*inv(5)
BIGNUM * By = BN_new ();
BN_set_word (By, 5);
Inv (By, ctx);
BN_mul_word (By, 4);
BIGNUM * Bx = RecoverX (By, ctx);
BN_mod (Bx, Bx, q, ctx); // % q
BN_mod (By, By, q, ctx); // % q
B = {Bx, By};
BN_free (two);
BN_free (tmp);
// precalculate Bi
Bi[0] = { BN_dup (Bx), BN_dup (By) };
for (int i = 1; i < 256; i++)
Bi[i] = Double (Bi[i-1], ctx);
BN_CTX_free (ctx);
} }
CryptoPP::ECP::Point DecodePublicKey (const uint8_t * key) const ~Ed25519 ()
{ {
return DecodePoint (CryptoPP::Integer (key, 32)); BN_free (q);
BN_free (l);
BN_free (d);
BN_free (I);
BN_free (q_2);
BN_free (two_252_2);
} }
CryptoPP::ECP::Point GeneratePublicKey (const uint8_t * privateKey) const
EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const
{ {
return Mul (B, CryptoPP::Integer (privateKey, 32)); return MulB (expandedPrivateKey, ctx); // left half of expanded key, considered as Little Endian
} }
private: EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const
{
return DecodePoint (buf, ctx);
}
void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf) const
{
EncodePoint (publicKey, buf);
}
CryptoPP::ECP::Point Sum (const CryptoPP::ECP::Point& p1, const CryptoPP::ECP::Point& p2) const bool Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature, BN_CTX * ctx) const
{ {
CryptoPP::Integer m = d*p1.x*p2.x*p1.y*p2.y, BIGNUM * h = DecodeBN (digest, 64);
x = a_times_b_mod_c (p1.x*p2.y + p2.x*p1.y, (CryptoPP::Integer::One() + m).InverseMod (q), q), // signature 0..31 - R, 32..63 - S
y = a_times_b_mod_c (p1.y*p2.y + p1.x*p2.x, (CryptoPP::Integer::One() - m).InverseMod (q), q); bool passed = MulB (signature + EDDSA25519_SIGNATURE_LENGTH/2, ctx) /*S*/ ==
return CryptoPP::ECP::Point {x, y}; Sum (DecodePoint (signature, ctx) /*R*/, Mul (publicKey, h, ctx), ctx);
BN_free (h);
if (!passed)
LogPrint (eLogError, "25519 signature verification failed");
return passed;
} }
CryptoPP::ECP::Point Mul (const CryptoPP::ECP::Point& p, const CryptoPP::Integer& e) const void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len,
uint8_t * signature, BN_CTX * bnCtx) const
{ {
CryptoPP::ECP::Point res {0, 1}; // calculate r
if (!e.IsZero ()) SHA512_CTX ctx;
SHA512_Init (&ctx);
SHA512_Update (&ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key
SHA512_Update (&ctx, buf, len); // data
uint8_t digest[64];
SHA512_Final (digest, &ctx);
BIGNUM * r = DecodeBN (digest, 32); // DecodeBN (digest, 64); // for test vectors
// calculate R
uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf
EncodePoint (MulB (digest, bnCtx), R); // EncodePoint (Mul (B, r, bnCtx), R); // for test vectors
// calculate S
SHA512_Init (&ctx);
SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update (&ctx, buf, len); // data
SHA512_Final (digest, &ctx);
BIGNUM * s = DecodeBN (digest, 64);
// S = (r + s*a) % l
BIGNUM * a = DecodeBN (expandedPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH); // left half of expanded key
BN_mul (s, s, a, bnCtx);
BN_add (s, s, r);
BN_mod (s, s, l, bnCtx); // % l
memcpy (signature, R, EDDSA25519_SIGNATURE_LENGTH/2);
EncodeBN (s, signature + EDDSA25519_SIGNATURE_LENGTH/2, EDDSA25519_SIGNATURE_LENGTH/2); // S
BN_free (r); BN_free (s); BN_free (a);
}
private:
EDDSAPoint Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const
{
BIGNUM * xx = BN_new (), * yy = BN_new ();
// m = d*p1.x*p2.x*p1.y*p2.y
BN_mul (xx, p1.x, p2.x, ctx);
BN_mul (yy, p1.y, p2.y, ctx);
BIGNUM * m = BN_dup (d);
BN_mul (m, m, xx, ctx);
BN_mul (m, m, yy, ctx);
// x = (p1.x*p2.y + p2.x*p1.y)*inv(1 + m)
// y = (p1.y*p2.y + p1.x*p2.x)*inv(1 - m)
// use one inversion instead two
// m1 = 1-m
BIGNUM * m1 = BN_new ();
BN_one (m1);
BN_sub (m1, m1, m);
// m = m+1
BN_add_word (m, 1);
// y = (p1.y*p2.y + p1.x*p2.x)*m
BIGNUM * y = BN_new ();
BN_add (y, xx, yy);
BN_mod_mul (y, y, m, q, ctx);
// x = (p1.x*p2.y + p2.x*p1.y)*m1
BIGNUM * x = BN_new ();
BN_mul (yy, p1.x, p2.y, ctx);
BN_mul (xx, p2.x, p1.y, ctx);
BN_add (x, xx, yy);
BN_mod_mul (x, x, m1, q, ctx);
// denominator m = m*m1
BN_mod_mul (m, m, m1, q, ctx);
Inv (m, ctx);
BN_mod_mul (x, x, m, q, ctx); // x = x/m
BN_mod_mul (y, y, m, q, ctx); // y = y/m
BN_free (xx);BN_free (yy); BN_free (m); BN_free (m1);
return EDDSAPoint {x, y};
}
EDDSAPoint Double (const EDDSAPoint& p, BN_CTX * ctx) const
{
BIGNUM * pxy = BN_new ();
BN_mul (pxy, p.x, p.y, ctx);
// m = d*(p.x*p.y)^2
BIGNUM * m = BN_new ();
BN_sqr (m, pxy, ctx);
BN_mul (m, m, d, ctx);
// x = (2*p.x*p.y)*inv(1 + m)
// y = (p.x^2 + p.y^2)*inv(1 - m)
// use one inversion instead two
// m1 = 1-m
BIGNUM * m1 = BN_new ();
BN_one (m1);
BN_sub (m1, m1, m);
// m = m+1
BN_add_word (m, 1);
// x = 2*p.x*p.y*m1
BN_mul_word (pxy, 2);
BIGNUM * x = BN_new ();
BN_mod_mul (x, pxy, m1, q, ctx);
// y = (p.x^2 + p.y^2)*m
BIGNUM * y = BN_new ();
BN_sqr (pxy, p.x, ctx);
BN_sqr (y, p.y, ctx);
BN_add (pxy, pxy, y);
BN_mod_mul (y, pxy, m, q, ctx);
// denominator m = m*m1
BN_mod_mul (m, m, m1, q, ctx);
Inv (m, ctx);
BN_mod_mul (x, x, m, q, ctx); // x = x/m
BN_mod_mul (y, y, m, q, ctx); // y = y/m
BN_free (pxy); BN_free (m); BN_free (m1);
return EDDSAPoint {x, y};
}
EDDSAPoint Mul (const EDDSAPoint& p, const BIGNUM * e, BN_CTX * ctx) const
{
BIGNUM * zero = BN_new (), * one = BN_new ();
BN_zero (zero); BN_one (one);
EDDSAPoint res {zero, one};
if (!BN_is_zero (e))
{ {
auto bitCount = e.BitCount (); int bitCount = BN_num_bits (e);
for (int i = bitCount - 1; i >= 0; i--) for (int i = bitCount - 1; i >= 0; i--)
{ {
res = Sum (res, res); res = Double (res, ctx);
if (e.GetBit (i)) res = Sum (res, p); if (BN_is_bit_set (e, i)) res = Sum (res, p, ctx);
} }
} }
return res; return res;
} }
EDDSAPoint MulB (const uint8_t * e, BN_CTX * ctx) const // B*e. e is 32 bytes Little Endian
{
BIGNUM * zero = BN_new (), * one = BN_new ();
BN_zero (zero); BN_one (one);
EDDSAPoint res {zero, one};
for (int i = 0; i < 32; i++)
{
for (int j = 0; j < 8; j++)
if (e[i] & (1 << j)) // from lowest to highest bit
res = Sum (res, Bi[i*8 + j], ctx);
}
return res;
}
void Inv (BIGNUM * x, BN_CTX * ctx) const
{
BN_mod_exp (x, x, q_2, q, ctx);
}
bool IsOnCurve (const CryptoPP::ECP::Point& p) const bool IsOnCurve (const EDDSAPoint& p, BN_CTX * ctx) const
{ {
auto x2 = p.x.Squared(), y2 = p.y.Squared (); BIGNUM * x2 = BN_new ();
return (y2 - x2 - CryptoPP::Integer::One() - d*x2*y2).Modulo (q).IsZero (); BN_sqr (x2, p.x, ctx); // x^2
BIGNUM * y2 = BN_new ();
BN_sqr (y2, p.y, ctx); // y^2
// y^2 - x^2 - 1 - d*x^2*y^2
BIGNUM * tmp = BN_new ();
BN_mul (tmp, d, x2, ctx);
BN_mul (tmp, tmp, y2, ctx);
BN_sub (tmp, y2, tmp);
BN_sub (tmp, tmp, x2);
BN_sub_word (tmp, 1);
BN_mod (tmp, tmp, q, ctx); // % q
bool ret = BN_is_zero (tmp);
BN_free (x2);
BN_free (y2);
BN_free (tmp);
return ret;
} }
CryptoPP::Integer RecoverX (const CryptoPP::Integer& y) const BIGNUM * RecoverX (const BIGNUM * y, BN_CTX * ctx) const
{ {
auto y2 = y.Squared (); BIGNUM * y2 = BN_new ();
auto xx = (y2 - CryptoPP::Integer::One())*(d*y2 + CryptoPP::Integer::One()).InverseMod (q); BN_sqr (y2, y, ctx); // y^2
auto x = a_exp_b_mod_c (xx, (q + CryptoPP::Integer (3)).DividedBy (8), q); // xx = (y^2 -1)*inv(d*y^2 +1)
if (!(x.Squared () - xx).Modulo (q).IsZero ()) BIGNUM * xx = BN_new ();
x = a_times_b_mod_c (x, I, q); BN_mul (xx, d, y2, ctx);
if (x.IsOdd ()) x = q - x; BN_add_word (xx, 1);
Inv (xx, ctx);
BN_sub_word (y2, 1);
BN_mul (xx, y2, xx, ctx);
// x = srqt(xx) = xx^(2^252-2)
BIGNUM * x = BN_new ();
BN_mod_exp (x, xx, two_252_2, q, ctx);
// check (x^2 -xx) % q
BN_sqr (y2, x, ctx);
BN_mod_sub (y2, y2, xx, q, ctx);
if (!BN_is_zero (y2))
BN_mod_mul (x, x, I, q, ctx);
if (BN_is_odd (x))
BN_sub (x, q, x);
BN_free (y2);
BN_free (xx);
return x; return x;
} }
CryptoPP::ECP::Point DecodePoint (const CryptoPP::Integer& y) const EDDSAPoint DecodePoint (const uint8_t * buf, BN_CTX * ctx) const
{ {
auto x = RecoverX (y); // buf is 32 bytes Little Endian, convert it to Big Endian
CryptoPP::ECP::Point p {x, y}; uint8_t buf1[EDDSA25519_PUBLIC_KEY_LENGTH];
if (!IsOnCurve (p)) for (size_t i = 0; i < EDDSA25519_PUBLIC_KEY_LENGTH/2; i++) // invert bytes
{ {
LogPrint (eLogError, "Decoded point is not on 25519"); buf1[i] = buf[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i];
return CryptoPP::ECP::Point {0, 1}; buf1[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i] = buf[i];
} }
bool isHighestBitSet = buf1[0] & 0x80;
if (isHighestBitSet)
buf1[0] &= 0x7f; // clear highest bit
BIGNUM * y = BN_new ();
BN_bin2bn (buf1, EDDSA25519_PUBLIC_KEY_LENGTH, y);
auto x = RecoverX (y, ctx);
if (BN_is_bit_set (x, 0) != isHighestBitSet)
BN_sub (x, q, x); // x = q - x
EDDSAPoint p {x, y};
if (!IsOnCurve (p, ctx))
LogPrint (eLogError, "Decoded point is not on 25519");
return p; return p;
} }
void EncodePoint (const EDDSAPoint& p, uint8_t * buf) const
{
EncodeBN (p.y, buf,EDDSA25519_PUBLIC_KEY_LENGTH);
if (BN_is_bit_set (p.x, 0)) // highest bit
buf[EDDSA25519_PUBLIC_KEY_LENGTH - 1] |= 0x80; // set highest bit
}
private: BIGNUM * DecodeBN (const uint8_t * buf, size_t len) const
{
// buf is Little Endian convert it to Big Endian
uint8_t buf1[len];
for (size_t i = 0; i < len/2; i++) // invert bytes
{
buf1[i] = buf[len -1 - i];
buf1[len -1 - i] = buf[i];
}
BIGNUM * res = BN_new ();
BN_bin2bn (buf1, len, res);
return res;
}
void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const
{
bn2buf (bn, buf, len);
// To Little Endian
for (size_t i = 0; i < len/2; i++) // invert bytes
{
uint8_t tmp = buf[i];
buf[i] = buf[len -1 - i];
buf[len -1 - i] = tmp;
}
}
CryptoPP::Integer q, l, d, I; private:
CryptoPP::ECP::Point B; // base point
BIGNUM * q, * l, * d, * I;
EDDSAPoint B; // base point
// transient values
BIGNUM * q_2; // q-2
BIGNUM * two_252_2; // 2^252-2
EDDSAPoint Bi[256]; // m_Bi[i] = 2^i*B for i-th bit
}; };
static std::unique_ptr<Ed25519> g_Ed25519; static std::unique_ptr<Ed25519> g_Ed25519;
@ -98,22 +374,44 @@ namespace crypto
if (!g_Ed25519) if (!g_Ed25519)
g_Ed25519.reset (new Ed25519 ()); g_Ed25519.reset (new Ed25519 ());
return g_Ed25519; return g_Ed25519;
} }
EDDSA25519Verifier::EDDSA25519Verifier (const uint8_t * signingKey): EDDSA25519Verifier::EDDSA25519Verifier (const uint8_t * signingKey):
m_PublicKey (GetEd25519 ()->DecodePublicKey (signingKey)) m_Ctx (BN_CTX_new ()),
m_PublicKey (GetEd25519 ()->DecodePublicKey (signingKey, m_Ctx))
{ {
memcpy (m_PublicKeyEncoded, signingKey, EDDSA25519_PUBLIC_KEY_LENGTH);
} }
bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{ {
return true; // TODO: SHA512_CTX ctx;
SHA512_Init (&ctx);
SHA512_Update (&ctx, signature, EDDSA25519_SIGNATURE_LENGTH/2); // R
SHA512_Update (&ctx, m_PublicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update (&ctx, buf, len); // data
uint8_t digest[64];
SHA512_Final (digest, &ctx);
return GetEd25519 ()->Verify (m_PublicKey, digest, signature, m_Ctx);
} }
void EDDSA25519Signer::Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey):
m_Ctx (BN_CTX_new ())
{
// expand key
SHA512 (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH, m_ExpandedPrivateKey);
m_ExpandedPrivateKey[0] &= 0xF8; // drop last 3 bits
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x1F; // drop first 3 bits
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit
// generate and encode public key
auto publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, m_Ctx);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded);
}
void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{ {
// TODO GetEd25519 ()->Sign (m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature, m_Ctx);
} }
} }
} }

@ -2,13 +2,15 @@
#define SIGNATURE_H__ #define SIGNATURE_H__
#include <inttypes.h> #include <inttypes.h>
#include <cryptopp/dsa.h> #include <string.h>
#include <cryptopp/rsa.h> #include <openssl/sha.h>
#include <cryptopp/asn.h> #include <openssl/dsa.h>
#include <cryptopp/oids.h> #include <openssl/ec.h>
#include <cryptopp/osrng.h> #include <openssl/ecdsa.h>
#include <cryptopp/eccrypto.h> #include <openssl/rsa.h>
#include "CryptoConst.h" #include <openssl/rand.h>
#include <openssl/evp.h>
#include "Crypto.h"
namespace i2p namespace i2p
{ {
@ -30,7 +32,7 @@ namespace crypto
public: public:
virtual ~Signer () {}; virtual ~Signer () {};
virtual void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const = 0; virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0;
}; };
const size_t DSA_PUBLIC_KEY_LENGTH = 128; const size_t DSA_PUBLIC_KEY_LENGTH = 128;
@ -42,13 +44,32 @@ namespace crypto
DSAVerifier (const uint8_t * signingKey) DSAVerifier (const uint8_t * signingKey)
{ {
m_PublicKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingKey, DSA_PUBLIC_KEY_LENGTH)); m_PublicKey = DSA_new ();
m_PublicKey->p = BN_dup (dsap);
m_PublicKey->q = BN_dup (dsaq);
m_PublicKey->g = BN_dup (dsag);
m_PublicKey->priv_key = NULL;
m_PublicKey->pub_key = BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL);
} }
~DSAVerifier ()
{
DSA_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{ {
CryptoPP::DSA::Verifier verifier (m_PublicKey); // calculate SHA1 digest
return verifier.VerifyMessage (buf, len, signature, DSA_SIGNATURE_LENGTH); uint8_t digest[20];
SHA1 (buf, len, digest);
// signature
DSA_SIG * sig = DSA_SIG_new();
sig->r = BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL);
sig->s = BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL);
// DSA verification
int ret = DSA_do_verify (digest, 20, sig, m_PublicKey);
DSA_SIG_free(sig);
return ret;
} }
size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; }; size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; };
@ -56,7 +77,7 @@ namespace crypto
private: private:
CryptoPP::DSA::PublicKey m_PublicKey; DSA * m_PublicKey;
}; };
class DSASigner: public Signer class DSASigner: public Signer
@ -65,189 +86,219 @@ namespace crypto
DSASigner (const uint8_t * signingPrivateKey) DSASigner (const uint8_t * signingPrivateKey)
{ {
m_PrivateKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH)); m_PrivateKey = DSA_new ();
m_PrivateKey->p = BN_dup (dsap);
m_PrivateKey->q = BN_dup (dsaq);
m_PrivateKey->g = BN_dup (dsag);
m_PrivateKey->priv_key = BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL);
m_PrivateKey->pub_key = NULL;
} }
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const ~DSASigner ()
{ {
CryptoPP::DSA::Signer signer (m_PrivateKey); DSA_free (m_PrivateKey);
signer.SignMessage (rnd, buf, len, signature); }
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
uint8_t digest[20];
SHA1 (buf, len, digest);
DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey);
bn2buf (sig->r, signature, DSA_SIGNATURE_LENGTH/2);
bn2buf (sig->s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
DSA_SIG_free(sig);
} }
private: private:
CryptoPP::DSA::PrivateKey m_PrivateKey; DSA * m_PrivateKey;
}; };
inline void CreateDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{ {
CryptoPP::DSA::PrivateKey privateKey; DSA * dsa = DSA_new ();
CryptoPP::DSA::PublicKey publicKey; dsa->p = BN_dup (dsap);
privateKey.Initialize (rnd, dsap, dsaq, dsag); dsa->q = BN_dup (dsaq);
privateKey.MakePublicKey (publicKey); dsa->g = BN_dup (dsag);
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH); dsa->priv_key = NULL;
publicKey.GetPublicElement ().Encode (signingPublicKey, DSA_PUBLIC_KEY_LENGTH); dsa->pub_key = NULL;
DSA_generate_key (dsa);
bn2buf (dsa->priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
bn2buf (dsa->pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
DSA_free (dsa);
} }
template<typename Hash, size_t keyLen> struct SHA256Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA256 (buf, len, digest);
}
enum { hashLen = 32 };
};
struct SHA384Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA384 (buf, len, digest);
}
enum { hashLen = 48 };
};
struct SHA512Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA512 (buf, len, digest);
}
enum { hashLen = 64 };
};
template<typename Hash, int curve, size_t keyLen>
class ECDSAVerifier: public Verifier class ECDSAVerifier: public Verifier
{ {
public: public:
template<typename Curve> ECDSAVerifier (const uint8_t * signingKey)
ECDSAVerifier (Curve curve, const uint8_t * signingKey)
{ {
m_PublicKey.Initialize (curve, m_PublicKey = EC_KEY_new_by_curve_name (curve);
CryptoPP::ECP::Point (CryptoPP::Integer (signingKey, keyLen/2), EC_KEY_set_public_key_affine_coordinates (m_PublicKey,
CryptoPP::Integer (signingKey + keyLen/2, keyLen/2))); BN_bin2bn (signingKey, keyLen/2, NULL),
BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL));
} }
~ECDSAVerifier ()
{
EC_KEY_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{ {
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::Verifier verifier (m_PublicKey); uint8_t digest[Hash::hashLen];
return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_SIG_new();
sig->r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
sig->s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
// ECDSA verification
int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey);
ECDSA_SIG_free(sig);
return ret;
} }
size_t GetPublicKeyLen () const { return keyLen; }; size_t GetPublicKeyLen () const { return keyLen; };
size_t GetSignatureLen () const { return keyLen; }; // signature length = key length size_t GetSignatureLen () const { return keyLen; }; // signature length = key length
private: private:
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PublicKey m_PublicKey; EC_KEY * m_PublicKey;
}; };
template<typename Hash> template<typename Hash, int curve, size_t keyLen>
class ECDSASigner: public Signer class ECDSASigner: public Signer
{ {
public: public:
template<typename Curve> ECDSASigner (const uint8_t * signingPrivateKey)
ECDSASigner (Curve curve, const uint8_t * signingPrivateKey, size_t keyLen)
{ {
m_PrivateKey.Initialize (curve, CryptoPP::Integer (signingPrivateKey, keyLen/2)); // private key length m_PrivateKey = EC_KEY_new_by_curve_name (curve);
EC_KEY_set_private_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen/2, NULL));
} }
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const ~ECDSASigner ()
{ {
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::Signer signer (m_PrivateKey); EC_KEY_free (m_PrivateKey);
signer.SignMessage (rnd, buf, len, signature); }
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey);
// signatureLen = keyLen
bn2buf (sig->r, signature, keyLen/2);
bn2buf (sig->s, signature + keyLen/2, keyLen/2);
ECDSA_SIG_free(sig);
} }
private: private:
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PrivateKey m_PrivateKey; EC_KEY * m_PrivateKey;
}; };
template<typename Hash, typename Curve> inline void CreateECDSARandomKeys (int curve, size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
inline void CreateECDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, Curve curve,
size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{ {
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PrivateKey privateKey; EC_KEY * signingKey = EC_KEY_new_by_curve_name (curve);
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PublicKey publicKey; EC_KEY_generate_key (signingKey);
privateKey.Initialize (rnd, curve); bn2buf (EC_KEY_get0_private_key (signingKey), signingPrivateKey, keyLen/2);
privateKey.MakePublicKey (publicKey); BIGNUM * x = BN_new(), * y = BN_new();
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, keyLen/2); EC_POINT_get_affine_coordinates_GFp (EC_KEY_get0_group(signingKey),
auto q = publicKey.GetPublicElement (); EC_KEY_get0_public_key (signingKey), x, y, NULL);
q.x.Encode (signingPublicKey, keyLen/2); bn2buf (x, signingPublicKey, keyLen/2);
q.y.Encode (signingPublicKey + keyLen/2, keyLen/2); bn2buf (y, signingPublicKey + keyLen/2, keyLen/2);
} BN_free (x); BN_free (y);
EC_KEY_free (signingKey);
}
// ECDSA_SHA256_P256 // ECDSA_SHA256_P256
const size_t ECDSAP256_KEY_LENGTH = 64; const size_t ECDSAP256_KEY_LENGTH = 64;
class ECDSAP256Verifier: public ECDSAVerifier<CryptoPP::SHA256, ECDSAP256_KEY_LENGTH> typedef ECDSAVerifier<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Verifier;
{ typedef ECDSASigner<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Signer;
public:
ECDSAP256Verifier (const uint8_t * signingKey):
ECDSAVerifier (CryptoPP::ASN1::secp256r1(), signingKey)
{
}
};
class ECDSAP256Signer: public ECDSASigner<CryptoPP::SHA256>
{
public:
ECDSAP256Signer (const uint8_t * signingPrivateKey): inline void CreateECDSAP256RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
ECDSASigner (CryptoPP::ASN1::secp256r1(), signingPrivateKey, ECDSAP256_KEY_LENGTH)
{
}
};
inline void CreateECDSAP256RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{ {
CreateECDSARandomKeys<CryptoPP::SHA256> (rnd, CryptoPP::ASN1::secp256r1(), ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey); CreateECDSARandomKeys (NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
} }
// ECDSA_SHA384_P384 // ECDSA_SHA384_P384
const size_t ECDSAP384_KEY_LENGTH = 96; const size_t ECDSAP384_KEY_LENGTH = 96;
class ECDSAP384Verifier: public ECDSAVerifier<CryptoPP::SHA384, ECDSAP384_KEY_LENGTH> typedef ECDSAVerifier<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Verifier;
{ typedef ECDSASigner<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Signer;
public:
ECDSAP384Verifier (const uint8_t * signingKey):
ECDSAVerifier (CryptoPP::ASN1::secp384r1(), signingKey)
{
}
};
class ECDSAP384Signer: public ECDSASigner<CryptoPP::SHA384> inline void CreateECDSAP384RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{ {
public: CreateECDSARandomKeys (NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
ECDSAP384Signer (const uint8_t * signingPrivateKey):
ECDSASigner (CryptoPP::ASN1::secp384r1(), signingPrivateKey, ECDSAP384_KEY_LENGTH)
{
}
};
inline void CreateECDSAP384RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
CreateECDSARandomKeys<CryptoPP::SHA384> (rnd, CryptoPP::ASN1::secp384r1(), ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
} }
// ECDSA_SHA512_P521 // ECDSA_SHA512_P521
const size_t ECDSAP521_KEY_LENGTH = 132; const size_t ECDSAP521_KEY_LENGTH = 132;
class ECDSAP521Verifier: public ECDSAVerifier<CryptoPP::SHA512, ECDSAP521_KEY_LENGTH> typedef ECDSAVerifier<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Verifier;
{ typedef ECDSASigner<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Signer;
public:
ECDSAP521Verifier (const uint8_t * signingKey): inline void CreateECDSAP521RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
ECDSAVerifier (CryptoPP::ASN1::secp521r1(), signingKey)
{
}
};
class ECDSAP521Signer: public ECDSASigner<CryptoPP::SHA512>
{
public:
ECDSAP521Signer (const uint8_t * signingPrivateKey):
ECDSASigner (CryptoPP::ASN1::secp521r1(), signingPrivateKey, ECDSAP521_KEY_LENGTH)
{
}
};
inline void CreateECDSAP521RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{ {
CreateECDSARandomKeys<CryptoPP::SHA512> (rnd, CryptoPP::ASN1::secp521r1(), ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey); CreateECDSARandomKeys (NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
} }
// RSA // RSA
template<typename Hash, size_t keyLen> template<typename Hash, int type, size_t keyLen>
class RSAVerifier: public Verifier class RSAVerifier: public Verifier
{ {
public: public:
RSAVerifier (const uint8_t * signingKey) RSAVerifier (const uint8_t * signingKey)
{ {
m_PublicKey.Initialize (CryptoPP::Integer (signingKey, keyLen), CryptoPP::Integer (rsae)); m_PublicKey = RSA_new ();
memset (m_PublicKey, 0, sizeof (RSA));
m_PublicKey->e = BN_dup (rsae);
m_PublicKey->n = BN_bin2bn (signingKey, keyLen, NULL);
} }
~RSAVerifier ()
{
RSA_free (m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{ {
typename CryptoPP::RSASS<CryptoPP::PKCS1v15, Hash>::Verifier verifier (m_PublicKey); uint8_t digest[Hash::hashLen];
return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length Hash::CalculateHash (buf, len, digest);
return RSA_verify (type, digest, Hash::hashLen, signature, GetSignatureLen (), m_PublicKey);
} }
size_t GetPublicKeyLen () const { return keyLen; } size_t GetPublicKeyLen () const { return keyLen; }
size_t GetSignatureLen () const { return keyLen; } size_t GetSignatureLen () const { return keyLen; }
@ -255,163 +306,92 @@ namespace crypto
private: private:
CryptoPP::RSA::PublicKey m_PublicKey; RSA * m_PublicKey;
}; };
template<typename Hash> template<typename Hash, int type, size_t keyLen>
class RSASigner: public Signer class RSASigner: public Signer
{ {
public: public:
RSASigner (const uint8_t * signingPrivateKey, size_t keyLen) RSASigner (const uint8_t * signingPrivateKey)
{ {
m_PrivateKey.Initialize (CryptoPP::Integer (signingPrivateKey, keyLen/2), m_PrivateKey = RSA_new ();
rsae, memset (m_PrivateKey, 0, sizeof (RSA));
CryptoPP::Integer (signingPrivateKey + keyLen/2, keyLen/2)); m_PrivateKey->e = BN_dup (rsae);
m_PrivateKey->n = BN_bin2bn (signingPrivateKey, keyLen, NULL);
m_PrivateKey->d = BN_bin2bn (signingPrivateKey + keyLen, keyLen, NULL);
} }
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const ~RSASigner ()
{ {
typename CryptoPP::RSASS<CryptoPP::PKCS1v15, Hash>::Signer signer (m_PrivateKey); RSA_free (m_PrivateKey);
signer.SignMessage (rnd, buf, len, signature); }
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest);
unsigned int signatureLen = keyLen;
RSA_sign (type, digest, Hash::hashLen, signature, &signatureLen, m_PrivateKey);
} }
private: private:
CryptoPP::RSA::PrivateKey m_PrivateKey; RSA * m_PrivateKey;
}; };
inline void CreateRSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, inline void CreateRSARandomKeys (size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{ {
CryptoPP::RSA::PrivateKey privateKey; RSA * rsa = RSA_new ();
privateKey.Initialize (rnd, publicKeyLen*8, rsae); RSA_generate_key_ex (rsa, publicKeyLen*8, rsae, NULL);
privateKey.GetModulus ().Encode (signingPrivateKey, publicKeyLen); bn2buf (rsa->n, signingPrivateKey, publicKeyLen);
privateKey.GetPrivateExponent ().Encode (signingPrivateKey + publicKeyLen, publicKeyLen); bn2buf (rsa->d, signingPrivateKey + publicKeyLen, publicKeyLen);
privateKey.GetModulus ().Encode (signingPublicKey, publicKeyLen); bn2buf (rsa->n, signingPublicKey, publicKeyLen);
RSA_free (rsa);
} }
// RSA_SHA256_2048 // RSA_SHA256_2048
const size_t RSASHA2562048_KEY_LENGTH = 256; const size_t RSASHA2562048_KEY_LENGTH = 256;
class RSASHA2562048Verifier: public RSAVerifier<CryptoPP::SHA256, RSASHA2562048_KEY_LENGTH> typedef RSAVerifier<SHA256Hash, NID_sha256, RSASHA2562048_KEY_LENGTH> RSASHA2562048Verifier;
{ typedef RSASigner<SHA256Hash, NID_sha256, RSASHA2562048_KEY_LENGTH> RSASHA2562048Signer;
public:
RSASHA2562048Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
{
}
};
class RSASHA2562048Signer: public RSASigner<CryptoPP::SHA256>
{
public:
RSASHA2562048Signer (const uint8_t * signingPrivateKey):
RSASigner (signingPrivateKey, RSASHA2562048_KEY_LENGTH*2)
{
}
};
// RSA_SHA384_3072 // RSA_SHA384_3072
const size_t RSASHA3843072_KEY_LENGTH = 384; const size_t RSASHA3843072_KEY_LENGTH = 384;
class RSASHA3843072Verifier: public RSAVerifier<CryptoPP::SHA384, RSASHA3843072_KEY_LENGTH> typedef RSAVerifier<SHA384Hash, NID_sha384, RSASHA3843072_KEY_LENGTH> RSASHA3843072Verifier;
{ typedef RSASigner<SHA384Hash, NID_sha384, RSASHA3843072_KEY_LENGTH> RSASHA3843072Signer;
public:
RSASHA3843072Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
{
}
};
class RSASHA3843072Signer: public RSASigner<CryptoPP::SHA384>
{
public:
RSASHA3843072Signer (const uint8_t * signingPrivateKey):
RSASigner (signingPrivateKey, RSASHA3843072_KEY_LENGTH*2)
{
}
};
// RSA_SHA512_4096 // RSA_SHA512_4096
const size_t RSASHA5124096_KEY_LENGTH = 512; const size_t RSASHA5124096_KEY_LENGTH = 512;
class RSASHA5124096Verifier: public RSAVerifier<CryptoPP::SHA512, RSASHA5124096_KEY_LENGTH> typedef RSAVerifier<SHA512Hash, NID_sha512, RSASHA5124096_KEY_LENGTH> RSASHA5124096Verifier;
{ typedef RSASigner<SHA512Hash, NID_sha512, RSASHA5124096_KEY_LENGTH> RSASHA5124096Signer;
public:
RSASHA5124096Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
{
}
};
class RSASHA5124096Signer: public RSASigner<CryptoPP::SHA512>
{
public:
RSASHA5124096Signer (const uint8_t * signingPrivateKey):
RSASigner (signingPrivateKey, RSASHA5124096_KEY_LENGTH*2)
{
}
};
// Raw verifiers // EdDSA
class RawVerifier struct EDDSAPoint
{
public:
virtual ~RawVerifier () {};
virtual void Update (const uint8_t * buf, size_t len) = 0;
virtual bool Verify (const uint8_t * signature) = 0;
};
template<typename Hash, size_t keyLen>
class RSARawVerifier: public RawVerifier
{ {
public: BIGNUM * x, * y;
EDDSAPoint (): x(nullptr), y(nullptr) {};
RSARawVerifier (const uint8_t * signingKey): EDDSAPoint (EDDSAPoint&& other): x(nullptr), y(nullptr)
n (signingKey, keyLen) { *this = std::move (other); };
{ EDDSAPoint (BIGNUM * x1, BIGNUM * y1): x(x1), y(y1) {};
} ~EDDSAPoint () { BN_free (x); BN_free (y); };
void Update (const uint8_t * buf, size_t len) EDDSAPoint& operator= (EDDSAPoint&& other)
{ {
m_Hash.Update (buf, len); if (x) BN_free (x);
} if (y) BN_free (y);
x = other.x; other.x = nullptr;
bool Verify (const uint8_t * signature) y = other.y; other.y = nullptr;
{ return *this;
// RSA encryption first }
CryptoPP::Integer enSig (a_exp_b_mod_c (CryptoPP::Integer (signature, keyLen),
CryptoPP::Integer (i2p::crypto::rsae), n)); // s^e mod n bool operator== (const EDDSAPoint& other) const
uint8_t enSigBuf[keyLen]; {
enSig.Encode (enSigBuf, keyLen); return !BN_cmp (x, other.x) && !BN_cmp (y, other.y);
}
uint8_t digest[Hash::DIGESTSIZE];
m_Hash.Final (digest);
if ((int)keyLen < Hash::DIGESTSIZE) return false; // can't verify digest longer than key
// we assume digest is right aligned, at least for PKCS#1 v1.5 padding
return !memcmp (enSigBuf + (keyLen - Hash::DIGESTSIZE), digest, Hash::DIGESTSIZE);
}
private:
CryptoPP::Integer n; // RSA modulus
Hash m_Hash;
}; };
class RSASHA5124096RawVerifier: public RSARawVerifier<CryptoPP::SHA512, RSASHA5124096_KEY_LENGTH>
{
public:
RSASHA5124096RawVerifier (const uint8_t * signingKey): RSARawVerifier (signingKey)
{
}
};
// EdDSA
const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32; const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32;
const size_t EDDSA25519_SIGNATURE_LENGTH = 64; const size_t EDDSA25519_SIGNATURE_LENGTH = 64;
const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32; const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32;
@ -420,24 +400,41 @@ namespace crypto
public: public:
EDDSA25519Verifier (const uint8_t * signingKey); EDDSA25519Verifier (const uint8_t * signingKey);
~EDDSA25519Verifier () { BN_CTX_free (m_Ctx); };
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; }; size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; }; size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; };
private: private:
CryptoPP::ECP::Point m_PublicKey; BN_CTX * m_Ctx;
EDDSAPoint m_PublicKey;
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
}; };
class EDDSA25519Signer: public Signer class EDDSA25519Signer: public Signer
{ {
public: public:
EDDSA25519Signer (const uint8_t * signingPrivateKey) {}; EDDSA25519Signer (const uint8_t * signingPrivateKey);
~EDDSA25519Signer () { BN_CTX_free (m_Ctx); };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; };
private:
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const; BN_CTX * m_Ctx;
uint8_t m_ExpandedPrivateKey[64];
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
}; };
inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
RAND_bytes (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
EDDSA25519Signer signer (signingPrivateKey);
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH);
}
} }
} }

@ -1,4 +1,4 @@
#include <cryptopp/gzip.h> #include <openssl/rand.h>
#include "Log.h" #include "Log.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "RouterContext.h" #include "RouterContext.h"
@ -20,7 +20,7 @@ namespace stream
m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO),
m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0) m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0)
{ {
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); RAND_bytes ((uint8_t *)&m_RecvStreamID, 4);
m_RemoteIdentity = remote->GetIdentity (); m_RemoteIdentity = remote->GetIdentity ();
m_CurrentRemoteLease.endDate = 0; m_CurrentRemoteLease.endDate = 0;
} }
@ -32,7 +32,7 @@ namespace stream
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE),
m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0) m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0)
{ {
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); RAND_bytes ((uint8_t *)&m_RecvStreamID, 4);
} }
Stream::~Stream () Stream::~Stream ()
@ -182,10 +182,11 @@ namespace stream
if (flags & PACKET_FLAG_FROM_INCLUDED) if (flags & PACKET_FLAG_FROM_INCLUDED)
{ {
optionData += m_RemoteIdentity.FromBuffer (optionData, packet->GetOptionSize ()); m_RemoteIdentity = std::make_shared<i2p::data::IdentityEx>(optionData, packet->GetOptionSize ());
LogPrint (eLogInfo, "From identity ", m_RemoteIdentity.GetIdentHash ().ToBase64 ()); optionData += m_RemoteIdentity->GetFullLen ();
LogPrint (eLogInfo, "From identity ", m_RemoteIdentity->GetIdentHash ().ToBase64 ());
if (!m_RemoteLeaseSet) if (!m_RemoteLeaseSet)
LogPrint (eLogDebug, "Incoming stream from ", m_RemoteIdentity.GetIdentHash ().ToBase64 ()); LogPrint (eLogDebug, "Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 ());
} }
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED)
@ -199,10 +200,10 @@ namespace stream
{ {
LogPrint (eLogDebug, "Signature"); LogPrint (eLogDebug, "Signature");
uint8_t signature[256]; uint8_t signature[256];
auto signatureLen = m_RemoteIdentity.GetSignatureLen (); auto signatureLen = m_RemoteIdentity->GetSignatureLen ();
memcpy (signature, optionData, signatureLen); memcpy (signature, optionData, signatureLen);
memset (const_cast<uint8_t *>(optionData), 0, signatureLen); memset (const_cast<uint8_t *>(optionData), 0, signatureLen);
if (!m_RemoteIdentity.Verify (packet->GetBuffer (), packet->GetLength (), signature)) if (!m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature))
{ {
LogPrint (eLogError, "Signature verification failed"); LogPrint (eLogError, "Signature verification failed");
Close (); Close ();
@ -353,11 +354,11 @@ namespace stream
if (isNoAck) flags |= PACKET_FLAG_NO_ACK; if (isNoAck) flags |= PACKET_FLAG_NO_ACK;
htobe16buf (packet + size, flags); htobe16buf (packet + size, flags);
size += 2; // flags size += 2; // flags
size_t identityLen = m_LocalDestination.GetOwner ().GetIdentity ().GetFullLen (); size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen ();
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen (); size_t signatureLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetSignatureLen ();
htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size
size += 2; // options size size += 2; // options size
m_LocalDestination.GetOwner ().GetIdentity ().ToBuffer (packet + size, identityLen); m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen);
size += identityLen; // from size += identityLen; // from
htobe16buf (packet + size, STREAMING_MTU); htobe16buf (packet + size, STREAMING_MTU);
size += 2; // max packet size size += 2; // max packet size
@ -366,7 +367,7 @@ namespace stream
size += signatureLen; // signature size += signatureLen; // signature
m_SendBuffer.read ((char *)(packet + size), STREAMING_MTU - size); m_SendBuffer.read ((char *)(packet + size), STREAMING_MTU - size);
size += m_SendBuffer.gcount (); // payload size += m_SendBuffer.gcount (); // payload
m_LocalDestination.GetOwner ().Sign (packet, size, signature); m_LocalDestination.GetOwner ()->Sign (packet, size, signature);
} }
else else
{ {
@ -528,13 +529,13 @@ namespace stream
size++; // resend delay size++; // resend delay
htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED); htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED);
size += 2; // flags size += 2; // flags
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen (); size_t signatureLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetSignatureLen ();
htobe16buf (packet + size, signatureLen); // signature only htobe16buf (packet + size, signatureLen); // signature only
size += 2; // options size size += 2; // options size
uint8_t * signature = packet + size; uint8_t * signature = packet + size;
memset (packet + size, 0, signatureLen); memset (packet + size, 0, signatureLen);
size += signatureLen; // signature size += signatureLen; // signature
m_LocalDestination.GetOwner ().Sign (packet, size, signature); m_LocalDestination.GetOwner ()->Sign (packet, size, signature);
p->len = size; p->len = size;
m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p)); m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p));
@ -597,15 +598,15 @@ namespace stream
} }
} }
if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ()) if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ())
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel); m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel);
if (!m_CurrentOutboundTunnel) if (!m_CurrentOutboundTunnel)
{ {
LogPrint (eLogError, "No outbound tunnels in the pool"); LogPrint (eLogError, "No outbound tunnels in the pool");
return; return;
} }
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (ts >= m_CurrentRemoteLease.endDate - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000) if (!m_CurrentRemoteLease.endDate || ts >= m_CurrentRemoteLease.endDate - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000)
UpdateCurrentRemoteLease (true); UpdateCurrentRemoteLease (true);
if (ts < m_CurrentRemoteLease.endDate) if (ts < m_CurrentRemoteLease.endDate)
{ {
@ -681,7 +682,7 @@ namespace stream
break; break;
case 3: case 3:
// pick another outbound tunnel // pick another outbound tunnel
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
LogPrint (eLogWarning, "Another outbound tunnel has been selected for stream"); LogPrint (eLogWarning, "Another outbound tunnel has been selected for stream");
break; break;
default: ; default: ;
@ -713,19 +714,19 @@ namespace stream
{ {
if (!m_RemoteLeaseSet) if (!m_RemoteLeaseSet)
{ {
m_RemoteLeaseSet = m_LocalDestination.GetOwner ().FindLeaseSet (m_RemoteIdentity.GetIdentHash ()); m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());
if (!m_RemoteLeaseSet) if (!m_RemoteLeaseSet)
LogPrint ("LeaseSet ", m_RemoteIdentity.GetIdentHash ().ToBase64 (), " not found"); LogPrint ("LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " not found");
} }
if (m_RemoteLeaseSet) if (m_RemoteLeaseSet)
{ {
if (!m_RoutingSession) if (!m_RoutingSession)
m_RoutingSession = m_LocalDestination.GetOwner ().GetRoutingSession (m_RemoteLeaseSet, 32); m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, 32);
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false); // try without threshold first auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false); // try without threshold first
if (leases.empty ()) if (leases.empty ())
{ {
expired = false; expired = false;
m_LocalDestination.GetOwner ().RequestDestination (m_RemoteIdentity.GetIdentHash ()); // time to re-request m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // time to re-request
leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold
} }
if (!leases.empty ()) if (!leases.empty ())
@ -743,7 +744,7 @@ namespace stream
} }
if (!updated) if (!updated)
{ {
uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1); uint32_t i = rand () % leases.size ();
if (m_CurrentRemoteLease.endDate && leases[i].tunnelID == m_CurrentRemoteLease.tunnelID) if (m_CurrentRemoteLease.endDate && leases[i].tunnelID == m_CurrentRemoteLease.tunnelID)
// make sure we don't select previous // make sure we don't select previous
i = (i + 1) % leases.size (); // if so, pick next i = (i + 1) % leases.size (); // if so, pick next
@ -764,27 +765,36 @@ namespace stream
std::shared_ptr<I2NPMessage> Stream::CreateDataMessage (const uint8_t * payload, size_t len) std::shared_ptr<I2NPMessage> Stream::CreateDataMessage (const uint8_t * payload, size_t len)
{ {
auto msg = ToSharedI2NPMessage (NewI2NPShortMessage ()); auto msg = ToSharedI2NPMessage (NewI2NPShortMessage ());
CryptoPP::Gzip compressor;
if (len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE) if (len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE)
compressor.SetDeflateLevel (CryptoPP::Gzip::MIN_DEFLATE_LEVEL); m_LocalDestination.m_Deflator.SetCompressionLevel (Z_NO_COMPRESSION);
else else
compressor.SetDeflateLevel (CryptoPP::Gzip::DEFAULT_DEFLATE_LEVEL); m_LocalDestination.m_Deflator.SetCompressionLevel (Z_DEFAULT_COMPRESSION);
compressor.Put (payload, len);
compressor.MessageEnd();
int size = compressor.MaxRetrievable ();
uint8_t * buf = msg->GetPayload (); uint8_t * buf = msg->GetPayload ();
htobe32buf (buf, size); // length buf += 4; // reserve for lengthlength
buf += 4; size_t size = m_LocalDestination.m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
compressor.Get (buf, size); if (size)
htobe16buf (buf + 4, m_LocalDestination.GetLocalPort ()); // source port {
htobe16buf (buf + 6, m_Port); // destination port htobe32buf (msg->GetPayload (), size); // length
buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol htobe16buf (buf + 4, m_LocalDestination.GetLocalPort ()); // source port
msg->len += size + 4; htobe16buf (buf + 6, m_Port); // destination port
msg->FillI2NPMessageHeader (eI2NPData); buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol
msg->len += size + 4;
msg->FillI2NPMessageHeader (eI2NPData);
}
else
msg = nullptr;
return msg; return msg;
} }
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort):
m_Owner (owner), m_LocalPort (localPort)
{
}
StreamingDestination::~StreamingDestination ()
{
}
void StreamingDestination::Start () void StreamingDestination::Start ()
{ {
} }
@ -845,7 +855,7 @@ namespace stream
std::shared_ptr<Stream> StreamingDestination::CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port) std::shared_ptr<Stream> StreamingDestination::CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{ {
auto s = std::make_shared<Stream> (m_Owner.GetService (), *this, remote, port); auto s = std::make_shared<Stream> (m_Owner->GetService (), *this, remote, port);
std::unique_lock<std::mutex> l(m_StreamsMutex); std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s; m_Streams[s->GetRecvStreamID ()] = s;
return s; return s;
@ -853,7 +863,7 @@ namespace stream
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream () std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream ()
{ {
auto s = std::make_shared<Stream> (m_Owner.GetService (), *this); auto s = std::make_shared<Stream> (m_Owner->GetService (), *this);
std::unique_lock<std::mutex> l(m_StreamsMutex); std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s; m_Streams[s->GetRecvStreamID ()] = s;
return s; return s;
@ -873,22 +883,13 @@ namespace stream
void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len) void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len)
{ {
// unzip it // unzip it
CryptoPP::Gunzip decompressor;
decompressor.Put (buf, len);
decompressor.MessageEnd();
Packet * uncompressed = new Packet; Packet * uncompressed = new Packet;
uncompressed->offset = 0; uncompressed->offset = 0;
uncompressed->len = decompressor.MaxRetrievable (); uncompressed->len = m_Inflator.Inflate (buf, len, uncompressed->buf, MAX_PACKET_SIZE);
if (uncompressed->len <= MAX_PACKET_SIZE) if (uncompressed->len)
{
decompressor.Get (uncompressed->buf, uncompressed->len);
HandleNextPacket (uncompressed); HandleNextPacket (uncompressed);
}
else else
{
LogPrint ("Received packet size ", uncompressed->len, " exceeds max packet size. Skipped");
delete uncompressed; delete uncompressed;
}
} }
} }
} }

@ -11,6 +11,7 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Base.h"
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Identity.h" #include "Identity.h"
#include "LeaseSet.h" #include "LeaseSet.h"
@ -107,7 +108,7 @@ namespace stream
uint32_t GetSendStreamID () const { return m_SendStreamID; }; uint32_t GetSendStreamID () const { return m_SendStreamID; };
uint32_t GetRecvStreamID () const { return m_RecvStreamID; }; uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
std::shared_ptr<const i2p::data::LeaseSet> GetRemoteLeaseSet () const { return m_RemoteLeaseSet; }; std::shared_ptr<const i2p::data::LeaseSet> GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
const i2p::data::IdentityEx& GetRemoteIdentity () const { return m_RemoteIdentity; }; std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () const { return m_RemoteIdentity; };
bool IsOpen () const { return m_Status == eStreamStatusOpen; }; bool IsOpen () const { return m_Status == eStreamStatusOpen; };
bool IsEstablished () const { return m_SendStreamID; }; bool IsEstablished () const { return m_SendStreamID; };
StreamStatus GetStatus () const { return m_Status; }; StreamStatus GetStatus () const { return m_Status; };
@ -166,7 +167,7 @@ namespace stream
StreamStatus m_Status; StreamStatus m_Status;
bool m_IsAckSendScheduled; bool m_IsAckSendScheduled;
StreamingDestination& m_LocalDestination; StreamingDestination& m_LocalDestination;
i2p::data::IdentityEx m_RemoteIdentity; std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet; std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession; std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
i2p::data::Lease m_CurrentRemoteLease; i2p::data::Lease m_CurrentRemoteLease;
@ -192,9 +193,8 @@ namespace stream
typedef std::function<void (std::shared_ptr<Stream>)> Acceptor; typedef std::function<void (std::shared_ptr<Stream>)> Acceptor;
StreamingDestination (i2p::client::ClientDestination& owner, uint16_t localPort = 0): StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0);
m_Owner (owner), m_LocalPort (localPort) {}; ~StreamingDestination ();
~StreamingDestination () {};
void Start (); void Start ();
void Stop (); void Stop ();
@ -204,7 +204,7 @@ namespace stream
void SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; }; void SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; };
void ResetAcceptor () { if (m_Acceptor) m_Acceptor (nullptr); m_Acceptor = nullptr; }; void ResetAcceptor () { if (m_Acceptor) m_Acceptor (nullptr); m_Acceptor = nullptr; };
bool IsAcceptorSet () const { return m_Acceptor != nullptr; }; bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
i2p::client::ClientDestination& GetOwner () { return m_Owner; }; std::shared_ptr<i2p::client::ClientDestination> GetOwner () const { return m_Owner; };
uint16_t GetLocalPort () const { return m_LocalPort; }; uint16_t GetLocalPort () const { return m_LocalPort; };
void HandleDataMessagePayload (const uint8_t * buf, size_t len); void HandleDataMessagePayload (const uint8_t * buf, size_t len);
@ -216,7 +216,7 @@ namespace stream
private: private:
i2p::client::ClientDestination& m_Owner; std::shared_ptr<i2p::client::ClientDestination> m_Owner;
uint16_t m_LocalPort; uint16_t m_LocalPort;
std::mutex m_StreamsMutex; std::mutex m_StreamsMutex;
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; std::map<uint32_t, std::shared_ptr<Stream> > m_Streams;
@ -224,6 +224,9 @@ namespace stream
public: public:
i2p::data::GzipInflator m_Inflator;
i2p::data::GzipDeflator m_Deflator;
// for HTTP only // for HTTP only
const decltype(m_Streams)& GetStreams () const { return m_Streams; }; const decltype(m_Streams)& GetStreams () const { return m_Streams; };
}; };

@ -14,8 +14,7 @@ namespace tunnel
TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, TransitTunnel::TransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey): const uint8_t * layerKey,const uint8_t * ivKey):
m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID), TunnelBase (receiveTunnelID, nextTunnelID, nextIdent)
m_NextIdent (nextIdent)
{ {
m_Encryption.SetKeys (layerKey, ivKey); m_Encryption.SetKeys (layerKey, ivKey);
} }
@ -54,12 +53,12 @@ namespace tunnel
void TransitTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) void TransitTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
{ {
LogPrint (eLogError, "We are not a gateway for transit tunnel ", m_TunnelID); LogPrint (eLogError, "We are not a gateway for transit tunnel ", GetTunnelID ());
} }
void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
{ {
LogPrint (eLogError, "Incoming tunnel message is not supported ", m_TunnelID); LogPrint (eLogError, "Incoming tunnel message is not supported ", GetTunnelID ());
} }
void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)

@ -5,7 +5,7 @@
#include <vector> #include <vector>
#include <mutex> #include <mutex>
#include <memory> #include <memory>
#include "aes.h" #include "Crypto.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "TunnelEndpoint.h" #include "TunnelEndpoint.h"
#include "TunnelGateway.h" #include "TunnelGateway.h"
@ -24,20 +24,12 @@ namespace tunnel
const uint8_t * layerKey,const uint8_t * ivKey); const uint8_t * layerKey,const uint8_t * ivKey);
virtual size_t GetNumTransmittedBytes () const { return 0; }; virtual size_t GetNumTransmittedBytes () const { return 0; };
uint32_t GetTunnelID () const { return m_TunnelID; };
// implements TunnelBase // implements TunnelBase
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg); void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg); void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out); void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
uint32_t GetNextTunnelID () const { return m_NextTunnelID; };
const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; };
private: private:
uint32_t m_TunnelID, m_NextTunnelID;
i2p::data::IdentHash m_NextIdent;
i2p::crypto::TunnelEncryption m_Encryption; i2p::crypto::TunnelEncryption m_Encryption;
}; };

@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "Identity.h" #include "Identity.h"
#include "Crypto.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
@ -13,17 +14,15 @@ namespace i2p
{ {
namespace transport namespace transport
{ {
struct DHKeysPair // transient keys for transport sessions
{
uint8_t publicKey[256];
uint8_t privateKey[256];
};
class SignedData class SignedData
{ {
public: public:
SignedData () {}; SignedData () {}
SignedData (const SignedData& other)
{
m_Stream << other.m_Stream.rdbuf ();
}
void Insert (const uint8_t * buf, size_t len) void Insert (const uint8_t * buf, size_t len)
{ {
m_Stream.write ((char *)buf, len); m_Stream.write ((char *)buf, len);
@ -35,9 +34,9 @@ namespace transport
m_Stream.write ((char *)&t, sizeof (T)); m_Stream.write ((char *)&t, sizeof (T));
} }
bool Verify (const i2p::data::IdentityEx& ident, const uint8_t * signature) const bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const
{ {
return ident.Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
} }
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const
@ -54,31 +53,31 @@ namespace transport
{ {
public: public:
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter): TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router):
m_RemoteRouter (in_RemoteRouter), m_DHKeysPair (nullptr), m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router)
m_NumSentBytes (0), m_NumReceivedBytes (0)
{ {
if (m_RemoteRouter) if (router)
m_RemoteIdentity = m_RemoteRouter->GetRouterIdentity (); m_RemoteIdentity = router->GetRouterIdentity ();
} }
virtual ~TransportSession () { delete m_DHKeysPair; }; virtual ~TransportSession () {};
virtual void Done () = 0; virtual void Done () = 0;
std::shared_ptr<const i2p::data::RouterInfo> GetRemoteRouter () { return m_RemoteRouter; }; std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; };
const i2p::data::IdentityEx& GetRemoteIdentity () { return m_RemoteIdentity; }; void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; };
size_t GetNumSentBytes () const { return m_NumSentBytes; }; size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
bool IsOutgoing () const { return m_IsOutgoing; };
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0; virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
protected: protected:
std::shared_ptr<const i2p::data::RouterInfo> m_RemoteRouter; std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
i2p::data::IdentityEx m_RemoteIdentity; std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
DHKeysPair * m_DHKeysPair; // X - for client and Y - for server
size_t m_NumSentBytes, m_NumReceivedBytes; size_t m_NumSentBytes, m_NumReceivedBytes;
bool m_IsOutgoing;
}; };
} }
} }

@ -1,6 +1,6 @@
#include <cryptopp/dh.h> #include <openssl/dh.h>
#include "Log.h" #include "Log.h"
#include "CryptoConst.h" #include "Crypto.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "NetDb.h" #include "NetDb.h"
@ -56,18 +56,18 @@ namespace transport
{ {
if (num > 0) if (num > 0)
{ {
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); i2p::crypto::DHKeys dh;
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
{ {
i2p::transport::DHKeysPair * pair = new i2p::transport::DHKeysPair (); auto pair = std::make_shared<i2p::crypto::DHKeys> ();
dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey); pair->GenerateKeys ();
std::unique_lock<std::mutex> l(m_AcquiredMutex); std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair); m_Queue.push (pair);
} }
} }
} }
DHKeysPair * DHKeysPairSupplier::Acquire () std::shared_ptr<i2p::crypto::DHKeys> DHKeysPairSupplier::Acquire ()
{ {
if (!m_Queue.empty ()) if (!m_Queue.empty ())
{ {
@ -78,15 +78,14 @@ namespace transport
return pair; return pair;
} }
else // queue is empty, create new else // queue is empty, create new
{ {
DHKeysPair * pair = new DHKeysPair (); auto pair = std::make_shared<i2p::crypto::DHKeys> ();
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); pair->GenerateKeys ();
dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey);
return pair; return pair;
} }
} }
void DHKeysPairSupplier::Return (DHKeysPair * pair) void DHKeysPairSupplier::Return (std::shared_ptr<i2p::crypto::DHKeys> pair)
{ {
std::unique_lock<std::mutex> l(m_AcquiredMutex); std::unique_lock<std::mutex> l(m_AcquiredMutex);
m_Queue.push (pair); m_Queue.push (pair);
@ -109,10 +108,6 @@ namespace transport
void Transports::Start () void Transports::Start ()
{ {
#ifdef USE_UPNP
m_UPnP.Start ();
LogPrint(eLogInfo, "UPnP started");
#endif
m_DHKeysPairSupplier.Start (); m_DHKeysPairSupplier.Start ();
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this)); m_Thread = new std::thread (std::bind (&Transports::Run, this));
@ -145,10 +140,6 @@ namespace transport
void Transports::Stop () void Transports::Stop ()
{ {
#ifdef USE_UPNP
m_UPnP.Stop ();
LogPrint(eLogInfo, "UPnP stopped");
#endif
m_PeerCleanupTimer.cancel (); m_PeerCleanupTimer.cancel ();
m_Peers.clear (); m_Peers.clear ();
if (m_SSUServer) if (m_SSUServer)
@ -412,13 +403,34 @@ namespace transport
else else
LogPrint (eLogError, "Can't detect external IP. SSU is not available"); LogPrint (eLogError, "Can't detect external IP. SSU is not available");
} }
DHKeysPair * Transports::GetNextDHKeysPair () void Transports::PeerTest ()
{
if (m_SSUServer)
{
bool statusChanged = false;
for (int i = 0; i < 5; i++)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter ();
if (router && router->IsSSU ())
{
if (!statusChanged)
{
statusChanged = true;
i2p::context.SetStatus (eRouterStatusTesting); // first time only
}
m_SSUServer->GetSession (router, true); // peer test
}
}
}
}
std::shared_ptr<i2p::crypto::DHKeys> Transports::GetNextDHKeysPair ()
{ {
return m_DHKeysPairSupplier.Acquire (); return m_DHKeysPairSupplier.Acquire ();
} }
void Transports::ReuseDHKeysPair (DHKeysPair * pair) void Transports::ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair)
{ {
m_DHKeysPairSupplier.Return (pair); m_DHKeysPairSupplier.Return (pair);
} }
@ -427,7 +439,8 @@ namespace transport
{ {
m_Service.post([session, this]() m_Service.post([session, this]()
{ {
auto ident = session->GetRemoteIdentity ().GetIdentHash (); if (!session->GetRemoteIdentity ()) return;
auto ident = session->GetRemoteIdentity ()->GetIdentHash ();
auto it = m_Peers.find (ident); auto it = m_Peers.find (ident);
if (it != m_Peers.end ()) if (it != m_Peers.end ())
{ {
@ -444,7 +457,8 @@ namespace transport
{ {
m_Service.post([session, this]() m_Service.post([session, this]()
{ {
auto ident = session->GetRemoteIdentity ().GetIdentHash (); if (!session->GetRemoteIdentity ()) return;
auto ident = session->GetRemoteIdentity ()->GetIdentHash ();
auto it = m_Peers.find (ident); auto it = m_Peers.find (ident);
if (it != m_Peers.end ()) if (it != m_Peers.end ())
{ {
@ -491,9 +505,9 @@ namespace transport
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
{ {
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); if (!m_Peers.size ()) return nullptr;
auto it = m_Peers.begin (); auto it = m_Peers.begin ();
std::advance (it, rnd.GenerateWord32 (0, m_Peers.size () - 1)); std::advance (it, rand () % m_Peers.size ());
return it != m_Peers.end () ? it->second.router : nullptr; return it != m_Peers.end () ? it->second.router : nullptr;
} }
} }

@ -11,7 +11,6 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <atomic> #include <atomic>
#include <cryptopp/osrng.h>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "TransportSession.h" #include "TransportSession.h"
#include "NTCPSession.h" #include "NTCPSession.h"
@ -20,10 +19,6 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Identity.h" #include "Identity.h"
#ifdef USE_UPNP
#include "UPnP.h"
#endif
namespace i2p namespace i2p
{ {
namespace transport namespace transport
@ -36,8 +31,8 @@ namespace transport
~DHKeysPairSupplier (); ~DHKeysPairSupplier ();
void Start (); void Start ();
void Stop (); void Stop ();
DHKeysPair * Acquire (); std::shared_ptr<i2p::crypto::DHKeys> Acquire ();
void Return (DHKeysPair * pair); void Return (std::shared_ptr<i2p::crypto::DHKeys> pair);
private: private:
@ -47,13 +42,12 @@ namespace transport
private: private:
const int m_QueueSize; const int m_QueueSize;
std::queue<DHKeysPair *> m_Queue; std::queue<std::shared_ptr<i2p::crypto::DHKeys> > m_Queue;
bool m_IsRunning; bool m_IsRunning;
std::thread * m_Thread; std::thread * m_Thread;
std::condition_variable m_Acquired; std::condition_variable m_Acquired;
std::mutex m_AcquiredMutex; std::mutex m_AcquiredMutex;
CryptoPP::AutoSeededRandomPool m_Rnd;
}; };
struct Peer struct Peer
@ -84,8 +78,8 @@ namespace transport
void Stop (); void Stop ();
boost::asio::io_service& GetService () { return m_Service; }; boost::asio::io_service& GetService () { return m_Service; };
i2p::transport::DHKeysPair * GetNextDHKeysPair (); std::shared_ptr<i2p::crypto::DHKeys> GetNextDHKeysPair ();
void ReuseDHKeysPair (DHKeysPair * pair); void ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair);
void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg); void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs); void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs);
@ -105,6 +99,8 @@ namespace transport
size_t GetNumPeers () const { return m_Peers.size (); }; size_t GetNumPeers () const { return m_Peers.size (); };
std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer () const; std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer () const;
void PeerTest ();
private: private:
void Run (); void Run ();
@ -141,10 +137,6 @@ namespace transport
uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes; uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes;
uint64_t m_LastBandwidthUpdateTime; uint64_t m_LastBandwidthUpdateTime;
#ifdef USE_UPNP
UPnP m_UPnP;
#endif
public: public:
// for HTTP only // for HTTP only

@ -3,7 +3,7 @@
#include <thread> #include <thread>
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
#include <cryptopp/sha.h> #include <openssl/rand.h>
#include "RouterContext.h" #include "RouterContext.h"
#include "Log.h" #include "Log.h"
#include "Timestamp.h" #include "Timestamp.h"
@ -18,6 +18,7 @@ namespace tunnel
{ {
Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config): Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config):
TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()),
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false) m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false)
{ {
} }
@ -28,7 +29,6 @@ namespace tunnel
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel) void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel)
{ {
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
auto numHops = m_Config->GetNumHops (); auto numHops = m_Config->GetNumHops ();
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops; int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops;
auto msg = NewI2NPShortMessage (); auto msg = NewI2NPShortMessage ();
@ -46,9 +46,13 @@ namespace tunnel
int i = 0; int i = 0;
while (hop) while (hop)
{ {
uint32_t msgID;
if (hop->next) // we set replyMsgID for last hop only
RAND_bytes ((uint8_t *)&msgID, 4);
else
msgID = replyMsgID;
int idx = recordIndicies[i]; int idx = recordIndicies[i];
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID);
hop->next ? rnd.GenerateWord32 () : replyMsgID); // we set replyMsgID for last hop only
hop->recordIndex = idx; hop->recordIndex = idx;
i++; i++;
hop = hop->next; hop = hop->next;
@ -57,7 +61,7 @@ namespace tunnel
for (int i = numHops; i < numRecords; i++) for (int i = numHops; i < numRecords; i++)
{ {
int idx = recordIndicies[i]; int idx = recordIndicies[i];
rnd.GenerateBlock (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE); RAND_bytes (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE);
} }
// decrypt real records // decrypt real records
@ -120,7 +124,9 @@ namespace tunnel
const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE; const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
uint8_t ret = record[BUILD_RESPONSE_RECORD_RET_OFFSET]; uint8_t ret = record[BUILD_RESPONSE_RECORD_RET_OFFSET];
LogPrint ("Ret code=", (int)ret); LogPrint ("Ret code=", (int)ret);
hop->router->GetProfile ()->TunnelBuildResponse (ret); auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ());
if (profile)
profile->TunnelBuildResponse (ret);
if (ret) if (ret)
// if any of participants declined the tunnel is not established // if any of participants declined the tunnel is not established
established = false; established = false;
@ -128,13 +134,16 @@ namespace tunnel
} }
if (established) if (established)
{ {
// change reply keys to layer keys // create tunnel decryptions from layer and iv keys in reverse order
hop = m_Config->GetFirstHop (); hop = m_Config->GetLastHop ();
while (hop) while (hop)
{ {
hop->decryption.SetKeys (hop->layerKey, hop->ivKey); auto tunnelHop = new TunnelHop{ .ident = hop->ident };
hop = hop->next; tunnelHop->decryption.SetKeys (hop->layerKey, hop->ivKey);
m_Hops.push_back (std::unique_ptr<TunnelHop>(tunnelHop));
hop = hop->prev;
} }
m_Config = nullptr;
} }
if (established) m_State = eTunnelStateEstablished; if (established) m_State = eTunnelStateEstablished;
return established; return established;
@ -144,13 +153,11 @@ namespace tunnel
{ {
const uint8_t * inPayload = in->GetPayload () + 4; const uint8_t * inPayload = in->GetPayload () + 4;
uint8_t * outPayload = out->GetPayload () + 4; uint8_t * outPayload = out->GetPayload () + 4;
TunnelHopConfig * hop = m_Config->GetLastHop (); for (auto& it: m_Hops)
while (hop) {
{ it->decryption.Decrypt (inPayload, outPayload);
hop->decryption.Decrypt (inPayload, outPayload);
hop = hop->prev;
inPayload = outPayload; inPayload = outPayload;
} }
} }
void Tunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) void Tunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
@ -158,6 +165,22 @@ namespace tunnel
LogPrint (eLogInfo, "Can't send I2NP messages without delivery instructions"); LogPrint (eLogInfo, "Can't send I2NP messages without delivery instructions");
} }
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > Tunnel::GetPeers () const
{
auto peers = GetInvertedPeers ();
std::reverse (peers.begin (), peers.end ());
return peers;
}
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > Tunnel::GetInvertedPeers () const
{
// hops are in inverted order
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > ret;
for (auto& it: m_Hops)
ret.push_back (it->ident);
return ret;
}
void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg) void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg)
{ {
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
@ -167,6 +190,13 @@ namespace tunnel
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
} }
void InboundTunnel::Print (std::stringstream& s) const
{
s << "-->" << i2p::data::GetIdentHashAbbreviation (GetNextIdentHash ()) << ":" << GetNextTunnelID ();
s << "-->" << (GetNumHops () - 1) << " hops ";
s << GetTunnelID () << ":me";
}
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg) void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg)
{ {
TunnelMessageBlock block; TunnelMessageBlock block;
@ -202,6 +232,12 @@ namespace tunnel
LogPrint (eLogError, "Incoming message for outbound tunnel ", GetTunnelID ()); LogPrint (eLogError, "Incoming message for outbound tunnel ", GetTunnelID ());
} }
void OutboundTunnel::Print (std::stringstream& s) const
{
s << "me-->" << i2p::data::GetIdentHashAbbreviation (GetNextIdentHash ()) << ":" << GetNextTunnelID ();
s << "-->" << (GetNumHops () - 1) << " hops-->";
}
Tunnels tunnels; Tunnels tunnels;
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr),
@ -272,8 +308,8 @@ namespace tunnel
std::shared_ptr<OutboundTunnel> Tunnels::GetNextOutboundTunnel () std::shared_ptr<OutboundTunnel> Tunnels::GetNextOutboundTunnel ()
{ {
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); if (!m_OutboundTunnels.size ()) return nullptr;
uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0; uint32_t ind = rand () % m_OutboundTunnels.size (), i = 0;
std::shared_ptr<OutboundTunnel> tunnel; std::shared_ptr<OutboundTunnel> tunnel;
for (auto it: m_OutboundTunnels) for (auto it: m_OutboundTunnels)
{ {
@ -482,8 +518,12 @@ namespace tunnel
auto hop = config->GetFirstHop (); auto hop = config->GetFirstHop ();
while (hop) while (hop)
{ {
if (hop->router) if (hop->ident)
hop->router->GetProfile ()->TunnelNonReplied (); {
auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ());
if (profile)
profile->TunnelNonReplied ();
}
hop = hop->next; hop = hop->next;
} }
} }
@ -553,8 +593,8 @@ namespace tunnel
if (!inboundTunnel || !router) return; if (!inboundTunnel || !router) return;
LogPrint ("Creating one hop outbound tunnel..."); LogPrint ("Creating one hop outbound tunnel...");
CreateTunnel<OutboundTunnel> ( CreateTunnel<OutboundTunnel> (
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > { router }, std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () },
inboundTunnel->GetTunnelConfig ()) inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ())
); );
} }
} }
@ -609,7 +649,7 @@ namespace tunnel
auto router = i2p::data::netdb.GetRandomRouter (); auto router = i2p::data::netdb.GetRandomRouter ();
LogPrint ("Creating one hop inbound tunnel..."); LogPrint ("Creating one hop inbound tunnel...");
CreateTunnel<InboundTunnel> ( CreateTunnel<InboundTunnel> (
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > { router }) std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () })
); );
} }
} }
@ -662,7 +702,8 @@ namespace tunnel
std::shared_ptr<TTunnel> Tunnels::CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel) std::shared_ptr<TTunnel> Tunnels::CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
{ {
auto newTunnel = std::make_shared<TTunnel> (config); auto newTunnel = std::make_shared<TTunnel> (config);
uint32_t replyMsgID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); uint32_t replyMsgID;
RAND_bytes ((uint8_t *)&replyMsgID, 4);
AddPendingTunnel (replyMsgID, newTunnel); AddPendingTunnel (replyMsgID, newTunnel);
newTunnel->Build (replyMsgID, outboundTunnel); newTunnel->Build (replyMsgID, outboundTunnel);
return newTunnel; return newTunnel;
@ -695,7 +736,9 @@ namespace tunnel
if (!pool) if (!pool)
{ {
// build symmetric outbound tunnel // build symmetric outbound tunnel
CreateTunnel<OutboundTunnel> (newTunnel->GetTunnelConfig ()->Invert (), GetNextOutboundTunnel ()); CreateTunnel<OutboundTunnel> (std::make_shared<TunnelConfig>(newTunnel->GetInvertedPeers (),
newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash ()),
GetNextOutboundTunnel ());
} }
else else
{ {
@ -710,9 +753,9 @@ namespace tunnel
void Tunnels::CreateZeroHopsInboundTunnel () void Tunnels::CreateZeroHopsInboundTunnel ()
{ {
CreateTunnel<InboundTunnel> ( CreateTunnel<InboundTunnel> (
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >
{ {
i2p::context.GetSharedRouterInfo () i2p::context.GetIdentity ()
})); }));
} }

@ -10,6 +10,7 @@
#include <mutex> #include <mutex>
#include <memory> #include <memory>
#include "Queue.h" #include "Queue.h"
#include "Crypto.h"
#include "TunnelConfig.h" #include "TunnelConfig.h"
#include "TunnelPool.h" #include "TunnelPool.h"
#include "TransitTunnel.h" #include "TransitTunnel.h"
@ -43,6 +44,12 @@ namespace tunnel
class InboundTunnel; class InboundTunnel;
class Tunnel: public TunnelBase class Tunnel: public TunnelBase
{ {
struct TunnelHop
{
std::shared_ptr<const i2p::data::IdentityEx> ident;
i2p::crypto::TunnelDecryption decryption;
};
public: public:
Tunnel (std::shared_ptr<const TunnelConfig> config); Tunnel (std::shared_ptr<const TunnelConfig> config);
@ -51,8 +58,11 @@ namespace tunnel
void Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr); void Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
std::shared_ptr<const TunnelConfig> GetTunnelConfig () const { return m_Config; } std::shared_ptr<const TunnelConfig> GetTunnelConfig () const { return m_Config; }
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const;
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetInvertedPeers () const;
TunnelState GetState () const { return m_State; }; TunnelState GetState () const { return m_State; };
void SetState (TunnelState state) { m_State = state; }; void SetState (TunnelState state) { m_State = state; };
int GetNumHops () const { return m_Hops.size (); }
bool IsEstablished () const { return m_State == eTunnelStateEstablished; }; bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
bool IsFailed () const { return m_State == eTunnelStateFailed; }; bool IsFailed () const { return m_State == eTunnelStateFailed; };
bool IsRecreated () const { return m_IsRecreated; }; bool IsRecreated () const { return m_IsRecreated; };
@ -66,12 +76,11 @@ namespace tunnel
// implements TunnelBase // implements TunnelBase
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg); void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out); void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
uint32_t GetNextTunnelID () const { return m_Config->GetFirstHop ()->tunnelID; };
const i2p::data::IdentHash& GetNextIdentHash () const { return m_Config->GetFirstHop ()->router->GetIdentHash (); };
private: private:
std::shared_ptr<const TunnelConfig> m_Config; std::shared_ptr<const TunnelConfig> m_Config;
std::vector<std::unique_ptr<TunnelHop> > m_Hops;
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
TunnelState m_State; TunnelState m_State;
bool m_IsRecreated; bool m_IsRecreated;
@ -81,22 +90,23 @@ namespace tunnel
{ {
public: public:
OutboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Gateway (this) {}; OutboundTunnel (std::shared_ptr<const TunnelConfig> config):
Tunnel (config), m_Gateway (this), m_EndpointIdentHash (config->GetLastIdentHash ()) {};
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg); void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg);
void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs); // multiple messages void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs); // multiple messages
std::shared_ptr<const i2p::data::RouterInfo> GetEndpointRouter () const const i2p::data::IdentHash& GetEndpointIdentHash () const { return m_EndpointIdentHash; };
{ return GetTunnelConfig ()->GetLastHop ()->router; };
size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); }; size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
void Print (std::stringstream& s) const;
// implements TunnelBase // implements TunnelBase
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg); void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
uint32_t GetTunnelID () const { return GetNextTunnelID (); };
private: private:
std::mutex m_SendMutex; std::mutex m_SendMutex;
TunnelGateway m_Gateway; TunnelGateway m_Gateway;
i2p::data::IdentHash m_EndpointIdentHash;
}; };
class InboundTunnel: public Tunnel, public std::enable_shared_from_this<InboundTunnel> class InboundTunnel: public Tunnel, public std::enable_shared_from_this<InboundTunnel>
@ -106,9 +116,8 @@ namespace tunnel
InboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Endpoint (true) {}; InboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Endpoint (true) {};
void HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg); void HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg);
size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
void Print (std::stringstream& s) const;
// implements TunnelBase
uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; };
private: private:
TunnelEndpoint m_Endpoint; TunnelEndpoint m_Endpoint;

@ -33,23 +33,26 @@ namespace tunnel
{ {
public: public:
//WARNING!!! GetSecondsSinceEpoch() return uint64_t TunnelBase (uint32_t tunnelID, uint32_t nextTunnelID, i2p::data::IdentHash nextIdent):
TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {}; m_TunnelID (tunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent),
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
virtual ~TunnelBase () {}; virtual ~TunnelBase () {};
virtual void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) = 0; virtual void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) = 0;
virtual void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) = 0; virtual void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) = 0;
virtual void FlushTunnelDataMsgs () {}; virtual void FlushTunnelDataMsgs () {};
virtual void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) = 0; virtual void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) = 0;
virtual uint32_t GetNextTunnelID () const = 0; uint32_t GetNextTunnelID () const { return m_NextTunnelID; };
virtual const i2p::data::IdentHash& GetNextIdentHash () const = 0; const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; };
virtual uint32_t GetTunnelID () const = 0; // as known at our side virtual uint32_t GetTunnelID () const { return m_TunnelID; }; // as known at our side
uint32_t GetCreationTime () const { return m_CreationTime; }; uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t t) { m_CreationTime = t; }; void SetCreationTime (uint32_t t) { m_CreationTime = t; };
private: private:
uint32_t m_TunnelID, m_NextTunnelID;
i2p::data::IdentHash m_NextIdent;
uint32_t m_CreationTime; // seconds since epoch uint32_t m_CreationTime; // seconds since epoch
}; };

@ -5,8 +5,9 @@
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include "aes.h" #include <openssl/rand.h>
#include "RouterInfo.h" #include "Crypto.h"
#include "Identity.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "Timestamp.h" #include "Timestamp.h"
@ -16,7 +17,8 @@ namespace tunnel
{ {
struct TunnelHopConfig struct TunnelHopConfig
{ {
std::shared_ptr<const i2p::data::RouterInfo> router, nextRouter; std::shared_ptr<const i2p::data::IdentityEx> ident;
i2p::data::IdentHash nextIdent;
uint32_t tunnelID, nextTunnelID; uint32_t tunnelID, nextTunnelID;
uint8_t layerKey[32]; uint8_t layerKey[32];
uint8_t ivKey[32]; uint8_t ivKey[32];
@ -25,19 +27,17 @@ 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 int recordIndex; // record # in tunnel build message
TunnelHopConfig (std::shared_ptr<const i2p::data::RouterInfo> r) TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r)
{ {
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); RAND_bytes (layerKey, 32);
rnd.GenerateBlock (layerKey, 32); RAND_bytes (ivKey, 32);
rnd.GenerateBlock (ivKey, 32); RAND_bytes (replyIV, 16);
rnd.GenerateBlock (replyIV, 16); RAND_bytes ((uint8_t *)&tunnelID, 4);
tunnelID = rnd.GenerateWord32 ();
isGateway = true; isGateway = true;
isEndpoint = true; isEndpoint = true;
router = r; ident = r;
//nextRouter = nullptr; //nextRouter = nullptr;
nextTunnelID = 0; nextTunnelID = 0;
@ -45,18 +45,17 @@ namespace tunnel
prev = nullptr; prev = nullptr;
} }
void SetNextRouter (std::shared_ptr<const i2p::data::RouterInfo> r) void SetNextIdent (const i2p::data::IdentHash& ident)
{ {
nextRouter = r; nextIdent = ident;
isEndpoint = false; isEndpoint = false;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); RAND_bytes ((uint8_t *)&nextTunnelID, 4);
nextTunnelID = rnd.GenerateWord32 ();
} }
void SetReplyHop (const TunnelHopConfig * replyFirstHop) void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent)
{ {
nextRouter = replyFirstHop->router; nextIdent = replyIdent;
nextTunnelID = replyFirstHop->tunnelID; nextTunnelID = replyTunnelID;
isEndpoint = true; isEndpoint = true;
} }
@ -68,7 +67,7 @@ namespace tunnel
next->prev = this; next->prev = this;
next->isGateway = false; next->isGateway = false;
isEndpoint = false; isEndpoint = false;
nextRouter = next->router; nextIdent = next->ident->GetIdentHash ();
nextTunnelID = next->tunnelID; nextTunnelID = next->tunnelID;
} }
} }
@ -88,9 +87,9 @@ namespace tunnel
{ {
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, router->GetIdentHash (), 32); memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, ident->GetIdentHash (), 32);
htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextRouter->GetIdentHash (), 32); memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32); memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32); memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32); memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
@ -102,8 +101,9 @@ namespace tunnel
htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ()); htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ());
htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
// TODO: fill padding // TODO: fill padding
router->GetElGamalEncryption ()->Encrypt (clearText, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET); i2p::crypto::ElGamalEncryption elGamalEncryption (ident->GetEncryptionPublicKey ());
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)router->GetIdentHash (), 16); elGamalEncryption.Encrypt (clearText, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
} }
}; };
@ -111,29 +111,18 @@ namespace tunnel
{ {
public: public:
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers) // inbound
{
CreatePeers (peers);
m_LastHop->SetNextIdent (i2p::context.GetIdentHash ());
}
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > peers, TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers,
std::shared_ptr<const TunnelConfig> replyTunnelConfig = nullptr) // replyTunnelConfig=nullptr means inbound uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent) // outbound
{ {
TunnelHopConfig * prev = nullptr; CreatePeers (peers);
for (auto it: peers) m_FirstHop->isGateway = false;
{ m_LastHop->SetReplyHop (replyTunnelID, replyIdent);
auto hop = new TunnelHopConfig (it);
if (prev)
prev->SetNext (hop);
else
m_FirstHop = hop;
prev = hop;
}
m_LastHop = prev;
if (replyTunnelConfig) // outbound
{
m_FirstHop->isGateway = false;
m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ());
}
else // inbound
m_LastHop->SetNextRouter (i2p::context.GetSharedRouterInfo ());
} }
~TunnelConfig () ~TunnelConfig ()
@ -172,13 +161,35 @@ namespace tunnel
bool IsInbound () const { return m_FirstHop->isGateway; } bool IsInbound () const { return m_FirstHop->isGateway; }
std::vector<std::shared_ptr<const i2p::data::RouterInfo> > GetPeers () const uint32_t GetTunnelID () const
{
if (!m_FirstHop) return 0;
return IsInbound () ? m_LastHop->nextTunnelID : m_FirstHop->tunnelID;
}
uint32_t GetNextTunnelID () const
{
if (!m_FirstHop) return 0;
return m_FirstHop->tunnelID;
}
const i2p::data::IdentHash& GetNextIdentHash () const
{
return m_FirstHop->ident->GetIdentHash ();
}
const i2p::data::IdentHash& GetLastIdentHash () const
{
return m_LastHop->ident->GetIdentHash ();
}
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const
{ {
std::vector<std::shared_ptr<const i2p::data::RouterInfo> > peers; std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
TunnelHopConfig * hop = m_FirstHop; TunnelHopConfig * hop = m_FirstHop;
while (hop) while (hop)
{ {
peers.push_back (hop->router); peers.push_back (hop->ident);
hop = hop->next; hop = hop->next;
} }
return peers; return peers;
@ -192,7 +203,7 @@ namespace tunnel
s << "-->" << m_FirstHop->tunnelID; s << "-->" << m_FirstHop->tunnelID;
while (hop) while (hop)
{ {
s << ":" << hop->router->GetIdentHashAbbreviation () << "-->"; s << ":" << GetIdentHashAbbreviation (hop->ident->GetIdentHash ()) << "-->";
if (!hop->isEndpoint) if (!hop->isEndpoint)
s << hop->nextTunnelID; s << hop->nextTunnelID;
else else
@ -202,19 +213,6 @@ namespace tunnel
// we didn't reach enpoint that mean we are last hop // we didn't reach enpoint that mean we are last hop
s << ":me"; s << ":me";
} }
std::shared_ptr<TunnelConfig> Invert () const
{
auto peers = GetPeers ();
std::reverse (peers.begin (), peers.end ());
// we use ourself as reply tunnel for outbound tunnel
return IsInbound () ? std::make_shared<TunnelConfig>(peers, shared_from_this ()) : std::make_shared<TunnelConfig>(peers);
}
std::shared_ptr<TunnelConfig> Clone (std::shared_ptr<const TunnelConfig> replyTunnelConfig = nullptr) const
{
return std::make_shared<TunnelConfig> (GetPeers (), replyTunnelConfig);
}
private: private:
@ -222,6 +220,22 @@ namespace tunnel
TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr) TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr)
{ {
} }
template<class Peers>
void CreatePeers (const Peers& peers)
{
TunnelHopConfig * prev = nullptr;
for (auto it: peers)
{
auto hop = new TunnelHopConfig (it);
if (prev)
prev->SetNext (hop);
else
m_FirstHop = hop;
prev = hop;
}
m_LastHop = prev;
}
private: private:

@ -1,5 +1,6 @@
#include "I2PEndian.h" #include "I2PEndian.h"
#include <string.h> #include <string.h>
#include <openssl/sha.h>
#include "Log.h" #include "Log.h"
#include "NetDb.h" #include "NetDb.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
@ -27,7 +28,7 @@ namespace tunnel
// verify checksum // verify checksum
memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end
uint8_t hash[32]; uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest (hash, fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16); // payload + iv SHA256(fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16, hash); // payload + iv
if (memcmp (hash, decrypted, 4)) if (memcmp (hash, decrypted, 4))
{ {
LogPrint (eLogError, "TunnelMessage: checksum verification failed"); LogPrint (eLogError, "TunnelMessage: checksum verification failed");

@ -1,6 +1,7 @@
#include <string.h> #include <string.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include <cryptopp/sha.h>
#include "Log.h" #include "Log.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "Transports.h" #include "Transports.h"
@ -13,7 +14,7 @@ namespace tunnel
TunnelGatewayBuffer::TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID), TunnelGatewayBuffer::TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID),
m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0)
{ {
context.GetRandomNumberGenerator ().GenerateBlock (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE); RAND_bytes (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE);
for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++) for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++)
if (!m_NonZeroRandomBuffer[i]) m_NonZeroRandomBuffer[i] = 1; if (!m_NonZeroRandomBuffer[i]) m_NonZeroRandomBuffer[i] = 1;
} }
@ -159,18 +160,17 @@ namespace tunnel
m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE; m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE;
uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload (); uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload ();
htobe32buf (buf, m_TunnelID); htobe32buf (buf, m_TunnelID);
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); RAND_bytes (buf + 4, 16); // original IV
rnd.GenerateBlock (buf + 4, 16); // original IV
memcpy (payload + size, buf + 4, 16); // copy IV for checksum memcpy (payload + size, buf + 4, 16); // copy IV for checksum
uint8_t hash[32]; uint8_t hash[32];
CryptoPP::SHA256().CalculateDigest (hash, payload, size+16); SHA256(payload, size+16, hash);
memcpy (buf+20, hash, 4); // checksum memcpy (buf+20, hash, 4); // checksum
payload[-1] = 0; // zero payload[-1] = 0; // zero
ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1 ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1
if (paddingSize > 0) if (paddingSize > 0)
{ {
// non-zero padding // non-zero padding
auto randomOffset = rnd.GenerateWord32 (0, TUNNEL_DATA_MAX_PAYLOAD_SIZE - paddingSize); auto randomOffset = rand () % (TUNNEL_DATA_MAX_PAYLOAD_SIZE - paddingSize + 1);
memcpy (buf + 24, m_NonZeroRandomBuffer + randomOffset, paddingSize); memcpy (buf + 24, m_NonZeroRandomBuffer + randomOffset, paddingSize);
} }

@ -1,11 +1,13 @@
#include <algorithm> #include <algorithm>
#include <openssl/rand.h>
#include "I2PEndian.h" #include "I2PEndian.h"
#include "CryptoConst.h" #include "Crypto.h"
#include "Tunnel.h" #include "Tunnel.h"
#include "NetDb.h" #include "NetDb.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "Garlic.h" #include "Garlic.h"
#include "Transports.h" #include "Transports.h"
#include "Log.h"
#include "TunnelPool.h" #include "TunnelPool.h"
namespace i2p namespace i2p
@ -141,8 +143,7 @@ namespace tunnel
typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const
{ {
if (tunnels.empty ()) return nullptr; if (tunnels.empty ()) return nullptr;
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0;
uint32_t ind = rnd.GenerateWord32 (0, tunnels.size ()/2), i = 0;
typename TTunnels::value_type tunnel = nullptr; typename TTunnels::value_type tunnel = nullptr;
for (auto it: tunnels) for (auto it: tunnels)
{ {
@ -165,7 +166,7 @@ namespace tunnel
{ {
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex); std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
for (auto it: m_OutboundTunnels) for (auto it: m_OutboundTunnels)
if (it->IsEstablished () && old->GetEndpointRouter ()->GetIdentHash () == it->GetEndpointRouter ()->GetIdentHash ()) if (it->IsEstablished () && old->GetEndpointIdentHash () == it->GetEndpointIdentHash ())
{ {
tunnel = it; tunnel = it;
break; break;
@ -200,7 +201,6 @@ namespace tunnel
void TunnelPool::TestTunnels () void TunnelPool::TestTunnels ()
{ {
auto& rnd = i2p::context.GetRandomNumberGenerator ();
for (auto it: m_Tests) for (auto it: m_Tests)
{ {
LogPrint ("Tunnel test ", (int)it.first, " failed"); LogPrint ("Tunnel test ", (int)it.first, " failed");
@ -251,7 +251,8 @@ namespace tunnel
} }
if (!failed) if (!failed)
{ {
uint32_t msgID = rnd.GenerateWord32 (); uint32_t msgID;
RAND_bytes ((uint8_t *)&msgID, 4);
m_Tests[msgID] = std::make_pair (*it1, *it2); m_Tests[msgID] = std::make_pair (*it1, *it2);
(*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (), (*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
CreateDeliveryStatusMsg (msgID)); CreateDeliveryStatusMsg (msgID));
@ -306,9 +307,9 @@ namespace tunnel
return hop; return hop;
} }
bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::RouterInfo> >& hops, bool isInbound) bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
{ {
if (m_ExplicitPeers) return SelectExplicitPeers (hops, isInbound); if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
auto prevHop = i2p::context.GetSharedRouterInfo (); auto prevHop = i2p::context.GetSharedRouterInfo ();
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
if (i2p::transport::transports.GetNumPeers () > 25) if (i2p::transport::transports.GetNumPeers () > 25)
@ -317,7 +318,7 @@ namespace tunnel
if (r && !r->GetProfile ()->IsBad ()) if (r && !r->GetProfile ()->IsBad ())
{ {
prevHop = r; prevHop = r;
hops.push_back (r); peers.push_back (r->GetRouterIdentity ());
numHops--; numHops--;
} }
} }
@ -331,12 +332,12 @@ namespace tunnel
return false; return false;
} }
prevHop = hop; prevHop = hop;
hops.push_back (hop); peers.push_back (hop->GetRouterIdentity ());
} }
return true; return true;
} }
bool TunnelPool::SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::RouterInfo> >& hops, bool isInbound) bool TunnelPool::SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
{ {
int size = m_ExplicitPeers->size (); int size = m_ExplicitPeers->size ();
std::vector<int> peerIndicies; std::vector<int> peerIndicies;
@ -349,7 +350,7 @@ namespace tunnel
auto& ident = (*m_ExplicitPeers)[peerIndicies[i]]; auto& ident = (*m_ExplicitPeers)[peerIndicies[i]];
auto r = i2p::data::netdb.FindRouter (ident); auto r = i2p::data::netdb.FindRouter (ident);
if (r) if (r)
hops.push_back (r); peers.push_back (r->GetRouterIdentity ());
else else
{ {
LogPrint (eLogInfo, "Can't find router for ", ident.ToBase64 ()); LogPrint (eLogInfo, "Can't find router for ", ident.ToBase64 ());
@ -366,11 +367,11 @@ namespace tunnel
if (!outboundTunnel) if (!outboundTunnel)
outboundTunnel = tunnels.GetNextOutboundTunnel (); outboundTunnel = tunnels.GetNextOutboundTunnel ();
LogPrint ("Creating destination inbound tunnel..."); LogPrint ("Creating destination inbound tunnel...");
std::vector<std::shared_ptr<const i2p::data::RouterInfo> > hops; std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
if (SelectPeers (hops, true)) if (SelectPeers (peers, true))
{ {
std::reverse (hops.begin (), hops.end ()); std::reverse (peers.begin (), peers.end ());
auto tunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig> (hops), outboundTunnel); auto tunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig> (peers), outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ()); tunnel->SetTunnelPool (shared_from_this ());
} }
else else
@ -383,7 +384,7 @@ namespace tunnel
if (!outboundTunnel) if (!outboundTunnel)
outboundTunnel = tunnels.GetNextOutboundTunnel (); outboundTunnel = tunnels.GetNextOutboundTunnel ();
LogPrint ("Re-creating destination inbound tunnel..."); LogPrint ("Re-creating destination inbound tunnel...");
auto newTunnel = tunnels.CreateTunnel<InboundTunnel> (tunnel->GetTunnelConfig ()->Clone (), outboundTunnel); auto newTunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig>(tunnel->GetPeers ()), outboundTunnel);
newTunnel->SetTunnelPool (shared_from_this()); newTunnel->SetTunnelPool (shared_from_this());
} }
@ -395,11 +396,11 @@ namespace tunnel
if (inboundTunnel) if (inboundTunnel)
{ {
LogPrint ("Creating destination outbound tunnel..."); LogPrint ("Creating destination outbound tunnel...");
std::vector<std::shared_ptr<const i2p::data::RouterInfo> > hops; std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
if (SelectPeers (hops, false)) if (SelectPeers (peers, false))
{ {
auto tunnel = tunnels.CreateTunnel<OutboundTunnel> ( auto tunnel = tunnels.CreateTunnel<OutboundTunnel> (
std::make_shared<TunnelConfig> (hops, inboundTunnel->GetTunnelConfig ())); std::make_shared<TunnelConfig> (peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()));
tunnel->SetTunnelPool (shared_from_this ()); tunnel->SetTunnelPool (shared_from_this ());
} }
else else
@ -418,7 +419,8 @@ namespace tunnel
{ {
LogPrint ("Re-creating destination outbound tunnel..."); LogPrint ("Re-creating destination outbound tunnel...");
auto newTunnel = tunnels.CreateTunnel<OutboundTunnel> ( auto newTunnel = tunnels.CreateTunnel<OutboundTunnel> (
tunnel->GetTunnelConfig ()->Clone (inboundTunnel->GetTunnelConfig ())); std::make_shared<TunnelConfig> (tunnel->GetPeers (),
inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()));
newTunnel->SetTunnelPool (shared_from_this ()); newTunnel->SetTunnelPool (shared_from_this ());
} }
else else
@ -428,7 +430,7 @@ namespace tunnel
void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel) void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel)
{ {
LogPrint (eLogInfo, "Creating paired inbound tunnel..."); LogPrint (eLogInfo, "Creating paired inbound tunnel...");
auto tunnel = tunnels.CreateTunnel<InboundTunnel> (outboundTunnel->GetTunnelConfig ()->Invert (), outboundTunnel); auto tunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ()); tunnel->SetTunnelPool (shared_from_this ());
} }
} }

@ -62,8 +62,8 @@ namespace tunnel
template<class TTunnels> template<class TTunnels>
typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const; typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const;
std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const; std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const;
bool SelectPeers (std::vector<std::shared_ptr<const i2p::data::RouterInfo> >& hops, bool isInbound); bool SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& hops, bool isInbound);
bool SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::RouterInfo> >& hops, bool isInbound); bool SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& hops, bool isInbound);
private: private:

@ -26,7 +26,7 @@ namespace api
if (logStream) if (logStream)
StartLog (logStream); StartLog (logStream);
else else
StartLog (i2p::util::filesystem::GetAppName () + ".log"); StartLog (i2p::util::filesystem::GetFullPath (i2p::util::filesystem::GetAppName () + ".log"));
i2p::data::netdb.Start(); i2p::data::netdb.Start();
LogPrint("NetDB started"); LogPrint("NetDB started");
i2p::transport::transports.Start(); i2p::transport::transports.Start();
@ -47,39 +47,41 @@ namespace api
StopLog (); StopLog ();
} }
i2p::client::ClientDestination * CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, void RunPeerTest ()
{
i2p::transport::transports.PeerTest ();
}
std::shared_ptr<i2p::client::ClientDestination> CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
const std::map<std::string, std::string> * params) const std::map<std::string, std::string> * params)
{ {
auto localDestination = new i2p::client::ClientDestination (keys, isPublic, params); auto localDestination = std::make_shared<i2p::client::ClientDestination> (keys, isPublic, params);
localDestination->Start (); localDestination->Start ();
return localDestination; return localDestination;
} }
i2p::client::ClientDestination * CreateLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType, std::shared_ptr<i2p::client::ClientDestination> CreateLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
const std::map<std::string, std::string> * params) const std::map<std::string, std::string> * params)
{ {
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
auto localDestination = new i2p::client::ClientDestination (keys, isPublic, params); auto localDestination = std::make_shared<i2p::client::ClientDestination> (keys, isPublic, params);
localDestination->Start (); localDestination->Start ();
return localDestination; return localDestination;
} }
void DestroyLocalDestination (i2p::client::ClientDestination * dest) void DestroyLocalDestination (std::shared_ptr<i2p::client::ClientDestination> dest)
{ {
if (dest) if (dest)
{
dest->Stop (); dest->Stop ();
delete dest;
}
} }
void RequestLeaseSet (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote) void RequestLeaseSet (std::shared_ptr<i2p::client::ClientDestination> dest, const i2p::data::IdentHash& remote)
{ {
if (dest) if (dest)
dest->RequestDestination (remote); dest->RequestDestination (remote);
} }
std::shared_ptr<i2p::stream::Stream> CreateStream (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote) std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<i2p::client::ClientDestination> dest, const i2p::data::IdentHash& remote)
{ {
if (!dest) return nullptr; if (!dest) return nullptr;
auto leaseSet = dest->FindLeaseSet (remote); auto leaseSet = dest->FindLeaseSet (remote);
@ -96,7 +98,7 @@ namespace api
} }
} }
void AcceptStream (i2p::client::ClientDestination * dest, const i2p::stream::StreamingDestination::Acceptor& acceptor) void AcceptStream (std::shared_ptr<i2p::client::ClientDestination> dest, const i2p::stream::StreamingDestination::Acceptor& acceptor)
{ {
if (dest) if (dest)
dest->AcceptStreams (acceptor); dest->AcceptStreams (acceptor);

15
api.h

@ -16,18 +16,19 @@ namespace api
void StartI2P (std::ostream * logStream = nullptr); void StartI2P (std::ostream * logStream = nullptr);
// write system log to logStream, if not specified to <appName>.log in application's folder // write system log to logStream, if not specified to <appName>.log in application's folder
void StopI2P (); void StopI2P ();
void RunPeerTest (); // should be called after UPnP
// destinations // destinations
i2p::client::ClientDestination * CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, std::shared_ptr<i2p::client::ClientDestination> CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
const std::map<std::string, std::string> * params = nullptr); const std::map<std::string, std::string> * params = nullptr);
i2p::client::ClientDestination * CreateLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256, std::shared_ptr<i2p::client::ClientDestination> CreateLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256,
const std::map<std::string, std::string> * params = nullptr); // transient destinations usually not published const std::map<std::string, std::string> * params = nullptr); // transient destinations usually not published
void DestroyLocalDestination (i2p::client::ClientDestination * dest); void DestroyLocalDestination (std::shared_ptr<i2p::client::ClientDestination> dest);
// streams // streams
void RequestLeaseSet (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote); void RequestLeaseSet (std::shared_ptr<i2p::client::ClientDestination> dest, const i2p::data::IdentHash& remote);
std::shared_ptr<i2p::stream::Stream> CreateStream (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote); std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<i2p::client::ClientDestination> dest, const i2p::data::IdentHash& remote);
void AcceptStream (i2p::client::ClientDestination * dest, const i2p::stream::StreamingDestination::Acceptor& acceptor); void AcceptStream (std::shared_ptr<i2p::client::ClientDestination> dest, const i2p::stream::StreamingDestination::Acceptor& acceptor);
void DestroyStream (std::shared_ptr<i2p::stream::Stream> stream); void DestroyStream (std::shared_ptr<i2p::stream::Stream> stream);
} }
} }

@ -1,22 +0,0 @@
#ifndef BASE64_H
#define BASE64_H
#include <inttypes.h>
#include <string.h>
namespace i2p
{
namespace data
{
size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len);
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
const char * GetBase64SubstitutionTable ();
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen);
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
}
}
#endif

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
MIIF1TCCA7+gAwIBAgIRAPmuHtSxMcNIUXNc0N4+7RIwCwYJKoZIhvcNAQELMG0x
CzAJBgNVBAYTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAK
BgNVBAsTA0kyUDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMRYwFAYDVQQDDA1w
YXJnQG1haWwuaTJwMB4XDTE1MDUxOTIwMjExM1oXDTI1MDUxOTIwMjExM1owbTEL
MAkGA1UEBhMCWFgxHjAcBgNVBAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoG
A1UECxMDSTJQMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgxFjAUBgNVBAMMDXBh
cmdAbWFpbC5pMnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/jINR
maFCnuCZwofrhtJoneIuJn6kauYYfjPF/nRrPffiooX2vo0Tp6cpTAulMul0Nh2I
/trJSbBqzneg+0zDS2wpLb1U7FJ5WGqb+yk7eo8X+Upjsuu4JoFr6ap81+J5oFBR
zTyYba6TYYwAZoBXAkY3qMjbfbYkqceY/p5WqAhEO7N4/DVLRA42FsQQMFwJYHnD
SgcyrTXiBbWJzvEF/4LSpL2CXB3Efkti/1MggVhXBu83PSkPvYQQTGFmwKP+ivVZ
V339xNGGKqPd+B1LOI8xUEAGGbOgfdB3c/x8weOwRip6bp+0SfLcVHO9X1lD95SA
dvtz2qABWDhqcMTyfJIEuOQSpQO6DhhBViHR2cjcu+z5Ugf+X6ZWhtFMBJsLb0Um
R3gKhPaMizCYVW6uRyA1B5SnO3Pd1ve1qX+K1h+NZPXoMpBxmyg+ds/WuhmAZUEB
bzUr7DczImb29WlBFCbYjA/fBN8QOc2qZpQFckY4CrBhCmFevxSpwHOxSkVEeXet
tYZ2BZIxN+I5p5SZc+6Ba1O3hqJsqv4D8H4TqXYZaw50ySRYIl9LDAEYp7owZzwA
oMxmjnsZBVtkGZpKk/RColQVLT/xmUqDJPQlWRw2te7tr1LiH2ZkVu7TUuhIep+i
4U8H4S9+zNQEAyn87L589AsGQ9hRtwrUOqy1ywIDAQABo3QwcjAOBgNVHQ8BAf8E
BAMCAIQwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA8GA1UdEwEB/wQF
MAMBAf8wFgYDVR0OBA8EDXBhcmdAbWFpbC5pMnAwGAYDVR0jBBEwD4ANcGFyZ0Bt
YWlsLmkycDALBgkqhkiG9w0BAQsDggIBAGaVJunmmO0EfDuTAN8Psjq7YVwwSyMk
6h1dMHz/U1GwY/jjKIIyfzKh6SfZmBfQT5fnGLM84m1O0267s9PZpHFcw9Kn+KQ4
YkfzVaYgsADjeyqQ1XIHJ/CZ60BWCs9aqWgYoTscbTtadaGscFBTegispkt8+Mj6
aaEQnajCD4f2GFHEi0miG+gRu6MDgqG2ka5tg3j+zfSDiq5lclq5qS97Lu/EVLRr
HlLKBDPnLKeGYnPOAlzTNqwWtvLho7jIFHt7DP1Qzn3nvDoOQb40ToHCAAcMr9jy
ncS6Eo4veeWeaSIGMnaDuzZoTWadizZo1G9z3ADMXRJ5IxdLKbBZiSkCHuAMnDSe
NKREmXewzjtRQBgf7RkyU0JwIqTKJsLTMX8oLecyvZHunmuKkqpJ/AgIyRB2X/nz
/LFeg4cru20Q+mpzVBnZPCS6X45jbew14ajURRgp4MrX52Ph+9/mS4RVQhHL+GDU
4OwF6tkqFD0umw1Gn1CAvKPOn9EVHa7nLDYxPo9LEX7Dnb5WkwKDqtALrcMDYjJr
4TqJFbsijYehVn+kFQ4I4aN54ECzwu9RKmebrXyDDe0e0fErRsF5+qII8/wOo4WT
XUjHCvK6THeaC8k5jcostgVszIx7rwqXj1ailbV/nAFr8NgADs04//5DJA0ieD+4
N1tGWBZt9Prq
-----END CERTIFICATE-----

@ -1,32 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFezCCA2OgAwIBAgIEPSs1SjANBgkqhkiG9w0BAQ0FADBuMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOc2luZHVAbWFpbC5p
MnAwHhcNMTQwNzIxMDAwNjUwWhcNMjQwNzIwMDAwNjUwWjBuMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOc2luZHVAbWFpbC5p
MnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnYMNclgj1gIJfot6f
65jbcXpcqf9hhL/uYiU4+uxVevWyJvNHpMzhPWNN4l3HihJJsINsPs/MsZ83Guva
GW5S/93I617kyjs/ZVpEtfABGewho0m9VCBV2/N1mJpHvvR+9OR+YVuWlTB/8sTG
2smdRj/dkKvewN5PSTQH350yT18NR/DmZUU1Iwa7vrNw8ol3rP3qx9UGpFN3JE7V
Q9cA1nktMiFUm76eOPOoln04WDqW2rvArXzvhSApvt0JsLBrZDzM3cx2Rc2UdjIC
h+Ha+G4CLjszfZfQAFJYPred38Gg6wuXiza/wCBSPiB92i94hIQF/OSeukaMiqwG
dRAcBT84/U9bddqHlIICw14PkNHOGUyJGjGKWQl/2bLX43ghWkUJmsTXS3iVcOTc
gb/7MoCRBdL0q2GyEJXuAoKXD9VqD3g+EdcBTQxS9lhZ0iTR7423pg6FP43VMEUC
HUi/BOX1tCY6iRzD1Su6ISIx7klH/sAWWa+SybLFXWtZJxHXXJICiBHJWRbWgtlu
5V+at66yg/LNpyfW3Am08gDV0kiWUBN2Ct4TX9PAQmNDisNgi2AzdZHIfX6tRpU8
UnNcnZGOh4+HXQwJtI0y83C8TsXJUFYfGFWqXN69sMEmgtX8/w+YUqjtb2GcX1HN
6z9u9lH40JCFHTA/clPqOSQ+MQIDAQABoyEwHzAdBgNVHQ4EFgQU4R6x7ArVpSVs
b8VTBXmodXzyraEwDQYJKoZIhvcNAQENBQADggIBAJEHLSDBRU2x6EalmN2W952p
MEO5lGD+ZfUVK0c44t1O53naffwZx9QmDmrC4TjeQrLOpAXLQ8GJHAGeZVmYRgkf
OioKde5uuqVcxqNxArO8VyYGwsuNVPCaBV+SyIO+EmWogidSIrOP2WsRRS2NBhLV
2dp3TvMeod9bPwRl00guvv9iqL0UVSpQSlfGkAQTVpyADOaQHOzeoCpmtPOfB6OK
syB/Z/6HElKoUbvhynaASLgmo3wM93PVJQ2Ho294bQHtDl2qcOksJQvWfCgi7Zrt
KuHaM/a2kItzI6JmyNFXgsKQSDJ4UvoppppgD7K48zOtSipGuZAADC5w5HdVvIGJ
1Czva8kTcmC6AMc+4tACGqYZEAEokkeXn+pIIqKVj2eQukT/0dLGGHbKmxp3Z0f2
pIH2Draq8JPdacr9P/xqEWUuViaOuC5OBjY8Fg3fmVCpwefIuk+DBhbJjEugB0Cu
brJpqNznoYahkbyAXIA8T+QJYMhoGWmaIcaPWK6K3nArvaxzwJbb9Egyivhyp9Rr
r2QMEZ+cPO8p1mEhKpL/wGqAzYyla8SJ06PzLc1lQeGiClu1nbZj5AgkZ1DLa8SD
iO7+e6rS0q1bzc7smE5JzZRiOVqKij/ReKa2uebLLI4wgAhz5ymaD1HfZY+3dV9T
WX89Xn2UyQf5kHifiDKL
-----END CERTIFICATE-----

@ -1,25 +1,16 @@
COMMON_SRC = \ LIB_SRC = \
CryptoConst.cpp Datagram.cpp Garlic.cpp I2NPProtocol.cpp LeaseSet.cpp \ Crypto.cpp Datagram.cpp Garlic.cpp I2NPProtocol.cpp LeaseSet.cpp \
Log.cpp NTCPSession.cpp NetDb.cpp NetDbRequests.cpp Profiling.cpp \ Log.cpp NTCPSession.cpp NetDb.cpp NetDbRequests.cpp Profiling.cpp \
Reseed.cpp RouterContext.cpp RouterInfo.cpp Signature.cpp SSU.cpp \ Reseed.cpp RouterContext.cpp RouterInfo.cpp Signature.cpp SSU.cpp \
SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \ SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \
Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp TunnelGateway.cpp \ Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp TunnelGateway.cpp \
Destination.cpp UPnP.cpp util.cpp aes.cpp base64.cpp Destination.cpp Base.cpp util.cpp api.cpp
ifeq ($(UNAME),Darwin)
# This is needed on OS X for some reason I don't understand (yet).
# Else will get linker error about unknown symbols. - torkel
COMMON_SRC += \
AddressBook.cpp BOB.cpp ClientContext.cpp Daemon.cpp I2PTunnel.cpp I2PService.cpp SAM.cpp SOCKS.cpp \
UPnP.cpp HTTPServer.cpp HTTPProxy.cpp i2p.cpp DaemonLinux.cpp I2PControl.cpp
endif
LIB_CLIENT_SRC = \
AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp \
SAM.cpp SOCKS.cpp HTTPProxy.cpp
# also: Daemon{Linux,Win32}.cpp will be added later # also: Daemon{Linux,Win32}.cpp will be added later
DAEMON_SRC = $(COMMON_SRC) \ DAEMON_SRC = \
AddressBook.cpp BOB.cpp ClientContext.cpp Daemon.cpp I2PTunnel.cpp I2PService.cpp \ HTTPServer.cpp I2PControl.cpp UPnP.cpp Daemon.cpp i2pd.cpp
SAM.cpp SOCKS.cpp HTTPServer.cpp HTTPProxy.cpp I2PControl.cpp i2p.cpp
LIB_SRC := $(COMMON_SRC) \
api.cpp

@ -1,61 +0,0 @@
#ifndef HMAC_H__
#define HMAC_H__
#include <inttypes.h>
#include <string.h>
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include <cryptopp/md5.h>
#include "Identity.h"
namespace i2p
{
namespace crypto
{
const uint64_t IPAD = 0x3636363636363636;
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
typedef i2p::data::Tag<32> MACKey;
inline void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest)
// key is 32 bytes
// digest is 16 bytes
// block size is 64 bytes
{
uint64_t buf[256];
// ikeypad
buf[0] = key.GetLL ()[0] ^ IPAD;
buf[1] = key.GetLL ()[1] ^ IPAD;
buf[2] = key.GetLL ()[2] ^ IPAD;
buf[3] = key.GetLL ()[3] ^ IPAD;
buf[4] = IPAD;
buf[5] = IPAD;
buf[6] = IPAD;
buf[7] = IPAD;
// concatenate with msg
memcpy (buf + 8, msg, len);
// calculate first hash
uint8_t hash[16]; // MD5
CryptoPP::Weak1::MD5().CalculateDigest (hash, (uint8_t *)buf, len + 64);
// okeypad
buf[0] = key.GetLL ()[0] ^ OPAD;
buf[1] = key.GetLL ()[1] ^ OPAD;
buf[2] = key.GetLL ()[2] ^ OPAD;
buf[3] = key.GetLL ()[3] ^ OPAD;
buf[4] = OPAD;
buf[5] = OPAD;
buf[6] = OPAD;
buf[7] = OPAD;
// copy first hash after okeypad
memcpy (buf + 8, hash, 16);
// fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P)
memset (buf + 10, 0, 16);
// calculate digest
CryptoPP::Weak1::MD5().CalculateDigest (digest, (uint8_t *)buf, 96);
}
}
}
#endif

@ -1,7 +1,6 @@
#include <thread> #include <thread>
#include <stdlib.h> #include <stdlib.h>
#include "Daemon.h" #include "Daemon.h"
#include "Reseed.h"
int main( int argc, char* argv[] ) int main( int argc, char* argv[] )
{ {
@ -10,10 +9,10 @@ int main( int argc, char* argv[] )
{ {
while (Daemon.running) while (Daemon.running)
{ {
//TODO Meeh: Find something better to do here.
std::this_thread::sleep_for (std::chrono::seconds(1)); std::this_thread::sleep_for (std::chrono::seconds(1));
} }
} }
Daemon.stop(); Daemon.stop();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

@ -2,7 +2,7 @@
#define _VERSION_H_ #define _VERSION_H_
#define CODENAME "Purple" #define CODENAME "Purple"
#define VERSION "0.10.0" #define VERSION "2.0.0"
#define I2P_VERSION "0.9.20" #define I2P_VERSION "0.9.22"
#endif #endif

Loading…
Cancel
Save