Merge pull request #28 from orignal/master

Merge pull request from orignal/master
This commit is contained in:
chertov 2014-04-09 10:35:33 +04:00
commit b6b9c61217
16 changed files with 342 additions and 187 deletions

View File

@ -80,7 +80,7 @@ void AddressBook::LoadHosts ()
getline(f, s); getline(f, s);
if (!s.length()) if (!s.length())
break; continue; // skip empty line
size_t pos = s.find('='); size_t pos = s.find('=');
@ -90,8 +90,11 @@ void AddressBook::LoadHosts ()
std::string addr = s.substr(pos); std::string addr = s.substr(pos);
Identity ident; Identity ident;
Base64ToByteStream (addr.c_str(), addr.length(), (uint8_t *)&ident, sizeof (ident)); if (!ident.FromBase64(addr)) {
m_Addresses[name] = CalculateIdentHash (ident); LogPrint ("hosts.txt: ignore ", name);
continue;
}
m_Addresses[name] = ident.Hash();
numAddresses++; numAddresses++;
} }
} }

View File

@ -6,6 +6,7 @@
#include <cryptopp/dsa.h> #include <cryptopp/dsa.h>
#include "CryptoConst.h" #include "CryptoConst.h"
#include "Identity.h" #include "Identity.h"
#include "base64.h"
namespace i2p namespace i2p
{ {
@ -19,6 +20,19 @@ namespace data
return *this; return *this;
} }
bool Identity::FromBase64 (const std::string& s)
{
size_t count = Base64ToByteStream (s.c_str(), s.length(), reinterpret_cast<uint8_t*> (this), sizeof (Identity));
return count == sizeof(Identity);
}
IdentHash Identity::Hash()
{
IdentHash hash;
CryptoPP::SHA256().CalculateDigest(reinterpret_cast<uint8_t*>(&hash), reinterpret_cast<uint8_t*> (this), sizeof (Identity));
return hash;
}
PrivateKeys& PrivateKeys::operator=(const Keys& keys) PrivateKeys& PrivateKeys::operator=(const Keys& keys)
{ {
pub = keys; pub = keys;
@ -26,13 +40,6 @@ namespace data
return *this; return *this;
} }
IdentHash CalculateIdentHash (const Identity& identity)
{
IdentHash hash;
CryptoPP::SHA256().CalculateDigest((uint8_t *)hash, (uint8_t *)&identity, sizeof (Identity));
return hash;
}
Keys CreateRandomKeys () Keys CreateRandomKeys ()
{ {
Keys keys; Keys keys;

View File

@ -9,6 +9,8 @@ namespace i2p
{ {
namespace data namespace data
{ {
class IdentHash;
#pragma pack(1) #pragma pack(1)
struct DHKeysPair // transient keys for transport sessions struct DHKeysPair // transient keys for transport sessions
@ -32,6 +34,8 @@ namespace data
uint8_t certificate[3]; uint8_t certificate[3];
Identity& operator=(const Keys& keys); Identity& operator=(const Keys& keys);
bool FromBase64(const std::string&);
IdentHash Hash();
}; };
struct PrivateKeys // for eepsites struct PrivateKeys // for eepsites
@ -75,7 +79,6 @@ namespace data
uint8_t m_Hash[32]; uint8_t m_Hash[32];
}; };
IdentHash CalculateIdentHash (const Identity& identity);
Keys CreateRandomKeys (); Keys CreateRandomKeys ();
void CreateRandomDHKeysPair (DHKeysPair * keys); // for transport sessions void CreateRandomDHKeysPair (DHKeysPair * keys); // for transport sessions
@ -103,7 +106,7 @@ namespace data
public: public:
RoutingDestination (): m_ElGamalEncryption (nullptr) {}; RoutingDestination (): m_ElGamalEncryption (nullptr) {};
virtual ~RoutingDestination () { delete m_ElGamalEncryption; }; virtual ~RoutingDestination () { if (m_ElGamalEncryption) delete m_ElGamalEncryption; };
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;
@ -125,6 +128,7 @@ namespace data
{ {
public: public:
virtual ~LocalDestination() {};
virtual const IdentHash& GetIdentHash () const = 0; virtual const IdentHash& GetIdentHash () const = 0;
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;

View File

@ -25,7 +25,7 @@ namespace data
const H * header = (const H *)buf; const H * header = (const H *)buf;
m_Identity = header->destination; m_Identity = header->destination;
m_IdentHash = CalculateIdentHash (m_Identity); m_IdentHash = m_Identity.Hash();
memcpy (m_EncryptionKey, header->encryptionKey, 256); memcpy (m_EncryptionKey, header->encryptionKey, 256);
LogPrint ("LeaseSet num=", (int)header->num); LogPrint ("LeaseSet num=", (int)header->num);

View File

@ -519,7 +519,7 @@ namespace ntcp
void NTCPSession::ScheduleTermination () void NTCPSession::ScheduleTermination ()
{ {
m_TerminationTimer.cancel (); m_TerminationTimer.cancel ();
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(TERMINATION_TIMEOUT)); m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_TIMEOUT));
m_TerminationTimer.async_wait (boost::bind (&NTCPSession::HandleTerminationTimer, m_TerminationTimer.async_wait (boost::bind (&NTCPSession::HandleTerminationTimer,
this, boost::asio::placeholders::error)); this, boost::asio::placeholders::error));
} }
@ -528,7 +528,7 @@ namespace ntcp
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
LogPrint ("No activity fo ", TERMINATION_TIMEOUT, " seconds"); LogPrint ("No activity fo ", NTCP_TERMINATION_TIMEOUT, " seconds");
m_Socket.close (); m_Socket.close ();
} }
} }

View File

@ -61,7 +61,7 @@ namespace ntcp
#pragma pack() #pragma pack()
const int TERMINATION_TIMEOUT = 120; // 2 minutes const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
class NTCPSession class NTCPSession
{ {
public: public:

View File

@ -34,7 +34,7 @@ namespace data
void RouterInfo::SetRouterIdentity (const Identity& identity) void RouterInfo::SetRouterIdentity (const Identity& identity)
{ {
m_RouterIdentity = identity; m_RouterIdentity = identity;
m_IdentHash = CalculateIdentHash (m_RouterIdentity); m_IdentHash = m_RouterIdentity.Hash ();
UpdateIdentHashBase64 (); UpdateIdentHashBase64 ();
UpdateRoutingKey (); UpdateRoutingKey ();
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
@ -129,6 +129,8 @@ namespace data
address.port = boost::lexical_cast<int>(value); address.port = boost::lexical_cast<int>(value);
else if (!strcmp (key, "key")) else if (!strcmp (key, "key"))
Base64ToByteStream (value, strlen (value), address.key, 32); Base64ToByteStream (value, strlen (value), address.key, 32);
else if (!strcmp (key, "caps"))
ExtractCaps (value);
else if (key[0] == 'i') else if (key[0] == 'i')
{ {
// introducers // introducers
@ -191,7 +193,6 @@ namespace data
void RouterInfo::ExtractCaps (const char * value) void RouterInfo::ExtractCaps (const char * value)
{ {
m_Caps = 0;
const char * cap = value; const char * cap = value;
while (*cap) while (*cap)
{ {
@ -208,6 +209,12 @@ namespace data
case 'R': case 'R':
m_Caps |= Caps::eReachable; m_Caps |= Caps::eReachable;
break; break;
case 'B':
m_Caps |= Caps::eSSUTesting;
break;
case 'C':
m_Caps |= Caps::eSSUIntroducer;
break;
default: ; default: ;
} }
cap++; cap++;
@ -249,7 +256,10 @@ namespace data
// caps // caps
WriteString ("caps", properties); WriteString ("caps", properties);
properties << '='; properties << '=';
WriteString ("B", properties); // TODO: should be 'BC' for introducers std::string caps;
if (IsPeerTesting ()) caps += 'B';
if (IsIntroducer ()) caps += 'C';
WriteString (caps, properties);
properties << ';'; properties << ';';
} }
else else
@ -344,11 +354,12 @@ namespace data
addr.host = boost::asio::ip::address::from_string (host); addr.host = boost::asio::ip::address::from_string (host);
addr.port = port; addr.port = port;
addr.transportStyle = eTransportSSU; addr.transportStyle = eTransportSSU;
addr.cost = 10; // NTCP should have prioprity over SSU addr.cost = 10; // NTCP should have priority over SSU
addr.date = 0; addr.date = 0;
memcpy (addr.key, key, 32); memcpy (addr.key, key, 32);
m_Addresses.push_back(addr); m_Addresses.push_back(addr);
m_SupportedTransports |= eSSUV4; m_SupportedTransports |= eSSUV4;
m_Caps |= eSSUTesting; // TODO
} }
void RouterInfo::SetProperty (const char * key, const char * value) void RouterInfo::SetProperty (const char * key, const char * value)

View File

@ -29,7 +29,9 @@ namespace data
{ {
eFloodfill = 0x01, eFloodfill = 0x01,
eHighBandwidth = 0x02, eHighBandwidth = 0x02,
eReachable = 0x04 eReachable = 0x04,
eSSUTesting = 0x08,
eSSUIntroducer = 0x10
}; };
enum TransportStyle enum TransportStyle
@ -84,6 +86,8 @@ namespace data
bool IsSSU (bool v4only = true) const; bool IsSSU (bool v4only = true) const;
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool UsesIntroducer () const; bool UsesIntroducer () const;
bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
uint8_t GetCaps () const { return m_Caps; }; uint8_t GetCaps () const { return m_Caps; };
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };

274
SSU.cpp
View File

@ -16,8 +16,10 @@ namespace ssu
{ {
SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
const i2p::data::RouterInfo * router): m_Server (server), m_RemoteEndpoint (remoteEndpoint), const i2p::data::RouterInfo * router, bool peerTest ):
m_RemoteRouter (router), m_Timer (m_Server.GetService ()), m_State (eSessionStateUnknown) m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_RemoteRouter (router),
m_Timer (m_Server.GetService ()), m_PeerTest (peerTest), m_State (eSessionStateUnknown),
m_RelayTag (0)
{ {
m_DHKeysPair = i2p::transports.GetNextDHKeysPair (); m_DHKeysPair = i2p::transports.GetNextDHKeysPair ();
} }
@ -57,16 +59,14 @@ namespace ssu
case eSessionStateConfirmedSent: case eSessionStateConfirmedSent:
case eSessionStateEstablished: case eSessionStateEstablished:
// most common case // most common case
ProcessMessage (buf, len); ScheduleTermination ();
ProcessMessage (buf, len, senderEndpoint);
break; break;
// establishing // establishing or testing
case eSessionStateUnknown: case eSessionStateUnknown:
// session request
ProcessSessionRequest (buf, len, senderEndpoint);
break;
case eSessionStateRequestSent: case eSessionStateRequestSent:
// session created // we must use intro key
ProcessSessionCreated (buf, len); ProcessIntroKeyMessage (buf, len, senderEndpoint);
break; break;
case eSessionStateCreatedSent: case eSessionStateCreatedSent:
// session confirmed // session confirmed
@ -92,21 +92,21 @@ namespace ssu
} }
} }
void SSUSession::ProcessMessage (uint8_t * buf, size_t len) void SSUSession::ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
if (Validate (buf, len, m_MacKey)) if (Validate (buf, len, m_MacKey))
{ {
Decrypt (buf, len, m_SessionKey); Decrypt (buf, len, m_SessionKey);
SSUHeader * header = (SSUHeader *)buf; SSUHeader * header = (SSUHeader *)buf;
uint8_t payloadType = header->flag >> 4; switch (header->GetPayloadType ())
switch (payloadType)
{ {
case PAYLOAD_TYPE_DATA: case PAYLOAD_TYPE_DATA:
LogPrint ("SSU data received"); LogPrint ("SSU data received");
ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader));
break; break;
case PAYLOAD_TYPE_TEST: case PAYLOAD_TYPE_PEER_TEST:
LogPrint ("SSU test received"); LogPrint ("SSU peer test received");
ProcessPeerTest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint);
break; break;
case PAYLOAD_TYPE_SESSION_DESTROYED: case PAYLOAD_TYPE_SESSION_DESTROYED:
{ {
@ -116,10 +116,10 @@ namespace ssu
} }
case PAYLOAD_TYPE_RELAY_INTRO: case PAYLOAD_TYPE_RELAY_INTRO:
LogPrint ("SSU relay intro received"); LogPrint ("SSU relay intro received");
// TODO: ProcessRelayIntro (buf + sizeof (SSUHeader), len - sizeof (SSUHeader));
break; break;
default: default:
LogPrint ("Unexpected SSU payload type ", (int)payloadType); LogPrint ("Unexpected SSU payload type ", (int)header->GetPayloadType ());
} }
} }
else else
@ -139,31 +139,57 @@ namespace ssu
} }
} }
void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) void SSUSession::ProcessIntroKeyMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
LogPrint ("Process session request"); auto introKey = GetIntroKey ();
// use our intro key if (!introKey)
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, buf, len)) {
LogPrint ("SSU is not supported");
return;
}
// use intro key for verification and decryption
if (!Validate (buf, len, introKey))
{
LogPrint ("MAC verification intro key failed");
Failed ();
return;
}
Decrypt (buf, len, introKey);
CreateAESandMacKey (buf + sizeof (SSUHeader), m_SessionKey, m_MacKey);
SSUHeader * header = (SSUHeader *)buf;
switch (header->GetPayloadType ())
{
case PAYLOAD_TYPE_SESSION_REQUEST:
ProcessSessionRequest (buf, len, senderEndpoint);
break;
case PAYLOAD_TYPE_SESSION_CREATED:
ProcessSessionCreated (buf, len);
break;
case PAYLOAD_TYPE_PEER_TEST:
LogPrint ("SSU peer test received");
// TODO:
break;
default: ;
}
}
void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{ {
m_State = eSessionStateRequestReceived; m_State = eSessionStateRequestReceived;
LogPrint ("Session request received"); LogPrint ("Session request received");
m_RemoteEndpoint = senderEndpoint; m_RemoteEndpoint = senderEndpoint;
SendSessionCreated (buf + sizeof (SSUHeader)); SendSessionCreated (buf + sizeof (SSUHeader));
} }
}
void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len) void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len)
{ {
LogPrint ("Process session created");
if (!m_RemoteRouter) if (!m_RemoteRouter)
{ {
LogPrint ("Unsolicited session created message"); LogPrint ("Unsolicited session created message");
return; return;
} }
// use remote intro key
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, buf, len))
{
m_State = eSessionStateCreatedReceived; m_State = eSessionStateCreatedReceived;
LogPrint ("Session created received"); LogPrint ("Session created received");
m_Timer.cancel (); // connect timer m_Timer.cancel (); // connect timer
@ -185,7 +211,7 @@ namespace ssu
*(uint32_t *)(signedData + 518) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ()); // remote IP *(uint32_t *)(signedData + 518) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ()); // remote IP
*(uint16_t *)(signedData + 522) = htobe16 (m_RemoteEndpoint.port ()); // remote port *(uint16_t *)(signedData + 522) = htobe16 (m_RemoteEndpoint.port ()); // remote port
memcpy (signedData + 524, payload, 8); // relayTag and signed on time memcpy (signedData + 524, payload, 8); // relayTag and signed on time
uint32_t relayTag = be32toh (*(uint32_t *)payload); m_RelayTag = be32toh (*(uint32_t *)payload);
payload += 4; // relayTag payload += 4; // relayTag
payload += 4; // signed on time payload += 4; // signed on time
// decrypt DSA signature // decrypt DSA signature
@ -198,8 +224,7 @@ namespace ssu
if (!verifier.VerifyMessage (signedData, 532, payload, 40)) if (!verifier.VerifyMessage (signedData, 532, payload, 40))
LogPrint ("SSU signature verification failed"); LogPrint ("SSU signature verification failed");
SendSessionConfirmed (y, ourAddress, relayTag); SendSessionConfirmed (y, ourAddress);
}
} }
void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len) void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len)
@ -209,7 +234,7 @@ namespace ssu
{ {
Decrypt (buf, len, m_SessionKey); Decrypt (buf, len, m_SessionKey);
SSUHeader * header = (SSUHeader *)buf; SSUHeader * header = (SSUHeader *)buf;
if ((header->flag >> 4) == PAYLOAD_TYPE_SESSION_CONFIRMED) if (header->GetPayloadType () == PAYLOAD_TYPE_SESSION_CONFIRMED)
{ {
m_State = eSessionStateConfirmedReceived; m_State = eSessionStateConfirmedReceived;
LogPrint ("Session confirmed received"); LogPrint ("Session confirmed received");
@ -326,7 +351,7 @@ namespace ssu
m_Server.Send (buf, 368, m_RemoteEndpoint); m_Server.Send (buf, 368, m_RemoteEndpoint);
} }
void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag) void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress)
{ {
uint8_t buf[480 + 18]; uint8_t buf[480 + 18];
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
@ -352,7 +377,7 @@ namespace ssu
memcpy (signedData + 512, ourAddress, 6); // our address/port as seem by party memcpy (signedData + 512, ourAddress, 6); // our address/port as seem by party
*(uint32_t *)(signedData + 518) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ()); // remote IP *(uint32_t *)(signedData + 518) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ()); // remote IP
*(uint16_t *)(signedData + 522) = htobe16 (m_RemoteEndpoint.port ()); // remote port *(uint16_t *)(signedData + 522) = htobe16 (m_RemoteEndpoint.port ()); // remote port
*(uint32_t *)(signedData + 524) = htobe32 (relayTag); // relay tag *(uint32_t *)(signedData + 524) = htobe32 (m_RelayTag); // relay tag
*(uint32_t *)(signedData + 528) = htobe32 (signedOnTime); // signed on time *(uint32_t *)(signedData + 528) = htobe32 (signedOnTime); // signed on time
i2p::context.Sign (signedData, 532, payload); // DSA signature i2p::context.Sign (signedData, 532, payload); // DSA signature
@ -406,33 +431,20 @@ namespace ssu
} }
} }
bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len) void SSUSession::ProcessRelayIntro (uint8_t * buf, size_t len)
{ {
auto introKey = GetIntroKey (); uint8_t size = *buf;
if (introKey) if (size == 4)
{ {
// use intro key for verification and decryption buf++; // size
if (Validate (buf, len, introKey)) boost::asio::ip::address_v4 address (be32toh (*(uint32_t* )buf));
{ buf += 4; // address
Decrypt (buf, len, introKey); uint16_t port = be16toh (*(uint16_t *)buf);
SSUHeader * header = (SSUHeader *)buf; // send hole punch of 1 byte
if ((header->flag >> 4) == expectedPayloadType) m_Server.Send (buf, 1, boost::asio::ip::udp::endpoint (address, port));
{
CreateAESandMacKey (buf + sizeof (SSUHeader), m_SessionKey, m_MacKey);
return true;
} }
else else
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4)); LogPrint ("Address size ", size, " is not supported");
}
else
{
LogPrint ("MAC verification failed");
Failed ();
}
}
else
LogPrint ("SSU is not supported");
return false;
} }
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len,
@ -544,6 +556,9 @@ namespace ssu
Send (it); Send (it);
m_DelayedMessages.clear (); m_DelayedMessages.clear ();
} }
if (m_PeerTest)
SendPeerTest ();
ScheduleTermination ();
} }
void SSUSession::Failed () void SSUSession::Failed ()
@ -556,6 +571,23 @@ namespace ssu
} }
} }
void SSUSession::ScheduleTermination ()
{
m_Timer.cancel ();
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_TIMEOUT));
m_Timer.async_wait (boost::bind (&SSUSession::HandleTerminationTimer,
this, boost::asio::placeholders::error));
}
void SSUSession::HandleTerminationTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint ("SSU no activity fo ", SSU_TERMINATION_TIMEOUT, " seconds");
Failed ();
}
}
const uint8_t * SSUSession::GetIntroKey () const const uint8_t * SSUSession::GetIntroKey () const
{ {
if (m_RemoteRouter) if (m_RemoteRouter)
@ -678,6 +710,87 @@ namespace ssu
} }
} }
void SSUSession::ProcessPeerTest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
{
uint8_t * buf1 = buf;
uint32_t nonce = be32toh (*(uint32_t *)buf);
buf += 4; // nonce
uint8_t size = *buf;
buf++; // size
uint8_t * address = (size == 4) ? buf : nullptr;
buf += size; // address
uint16_t port = *(uint16_t *)buf; // use it as is
buf += 2; // port
uint8_t * introKey = buf;
if (port)
{
LogPrint ("SSU peer test. We are Charlie");
Send (PAYLOAD_TYPE_PEER_TEST, buf1, len); // back to Bob
if (address)
SendPeerTest (nonce, be32toh (*(uint32_t *)address), be16toh (port), introKey); // to Alice
else
LogPrint ("Address of ", size, " bytes not supported");
}
else
{
LogPrint ("SSU peer test. We are Bob");
// TODO:
}
}
void SSUSession::SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, uint8_t * introKey)
{
uint8_t buf[80 + 18];
uint8_t iv[16];
uint8_t * payload = buf + sizeof (SSUHeader);
*(uint32_t *)payload = htobe32 (nonce);
payload += 4; // nonce
*payload = 4;
payload++; // size
*(uint32_t *)payload = htobe32 (address);
payload += 4; // address
*(uint16_t *)payload = htobe32 (port);
payload += 2; // port
memcpy (payload, introKey, 32); // intro key
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with specified intro key
FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey);
boost::asio::ip::udp::endpoint e (boost::asio::ip::address_v4 (address), port);
m_Server.Send (buf, 80, e);
}
void SSUSession::SendPeerTest ()
{
LogPrint ("SSU sending peer test");
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (!address)
{
LogPrint ("SSU is not supported. Can't send peer test");
return;
}
auto introKey = address->key;
uint8_t buf[80 + 18];
uint8_t * payload = buf + sizeof (SSUHeader);
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
uint32_t nonce = 0;
rnd.GenerateWord32 (nonce);
*(uint32_t *)payload = htobe32 (nonce);
payload += 4; // nonce
*payload = 4;
payload++; // size
memset (payload, 0, 6); // address and port always zero for Alice
payload += 6; // address and port
memcpy (payload, introKey, 32); // intro key
uint8_t iv[16];
rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, m_SessionKey, iv, m_MacKey);
m_Server.Send (buf, 80, m_RemoteEndpoint);
}
void SSUSession::SendMsgAck (uint32_t msgID) void SSUSession::SendMsgAck (uint32_t msgID)
{ {
uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16 uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16
@ -704,22 +817,12 @@ namespace ssu
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv rnd.GenerateBlock (iv, 16); // random iv
if (m_State == eSessionStateEstablished) if (m_State == eSessionStateEstablished)
{
// encrypt message with session key // encrypt message with session key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey); FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey);
else
{
auto introKey = GetIntroKey ();
if (introKey)
// encrypt message with intro key
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, introKey, iv, introKey);
else
{
LogPrint ("SSU: can't send SessionDestroyed message");
return;
}
}
m_Server.Send (buf, 48, m_RemoteEndpoint); m_Server.Send (buf, 48, m_RemoteEndpoint);
} }
}
void SSUSession::Send (i2p::I2NPMessage * msg) void SSUSession::Send (i2p::I2NPMessage * msg)
{ {
@ -771,6 +874,24 @@ namespace ssu
} }
} }
void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len)
{
uint8_t buf[SSU_MTU + 18];
uint8_t iv[16];
size_t msgSize = len + sizeof (SSUHeader);
if (msgSize > SSU_MTU)
{
LogPrint ("SSU payload size ", msgSize, " exceeds MTU");
return;
}
memcpy (buf + sizeof (SSUHeader), payload, len);
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv
// encrypt message with session key
FillHeaderAndEncrypt (type, buf, msgSize, m_SessionKey, iv, m_MacKey);
m_Server.Send (buf, msgSize, m_RemoteEndpoint);
}
SSUServer::SSUServer (boost::asio::io_service& service, int port): SSUServer::SSUServer (boost::asio::io_service& service, int port):
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (service, m_Endpoint) m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (service, m_Endpoint)
{ {
@ -841,7 +962,7 @@ namespace ssu
return nullptr; return nullptr;
} }
SSUSession * SSUServer::GetSession (const i2p::data::RouterInfo * router) SSUSession * SSUServer::GetSession (const i2p::data::RouterInfo * router, bool peerTest)
{ {
SSUSession * session = nullptr; SSUSession * session = nullptr;
if (router) if (router)
@ -859,10 +980,10 @@ namespace ssu
if (!router->UsesIntroducer ()) if (!router->UsesIntroducer ())
{ {
// connect directly // connect directly
session = new SSUSession (*this, remoteEndpoint, router); session = new SSUSession (*this, remoteEndpoint, router, peerTest);
m_Sessions[remoteEndpoint] = session; m_Sessions[remoteEndpoint] = session;
LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (), "] ", LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port (), " created"); remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
session->Connect (); session->Connect ();
} }
else else
@ -872,12 +993,21 @@ namespace ssu
{ {
auto& introducer = address->introducers[0]; // TODO: auto& introducer = address->introducers[0]; // TODO:
boost::asio::ip::udp::endpoint introducerEndpoint (introducer.iHost, introducer.iPort); boost::asio::ip::udp::endpoint introducerEndpoint (introducer.iHost, introducer.iPort);
it = m_Sessions.find (introducerEndpoint);
if (it == m_Sessions.end ())
{
session = new SSUSession (*this, introducerEndpoint, router); session = new SSUSession (*this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = session; m_Sessions[introducerEndpoint] = session;
LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (), LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (),
"] through introducer ", introducerEndpoint.address ().to_string (), ":", introducerEndpoint.port ()); "] through introducer ", introducerEndpoint.address ().to_string (), ":", introducerEndpoint.port ());
session->ConnectThroughIntroducer (introducer); session->ConnectThroughIntroducer (introducer);
} }
else
{
LogPrint ("Session to introducer already exists");
// TODO:
}
}
else else
LogPrint ("Router is unreachable, but not introducers presentd. Ignored"); LogPrint ("Router is unreachable, but not introducers presentd. Ignored");
} }
@ -917,7 +1047,7 @@ namespace ssu
auto session = it->second; auto session = it->second;
m_Sessions.erase (it); m_Sessions.erase (it);
m_Sessions[newEndpoint] = session; m_Sessions[newEndpoint] = session;
LogPrint ("SSU session ressigned from ", oldEndpoint.address ().to_string (), ":", oldEndpoint.port (), LogPrint ("SSU session reassigned from ", oldEndpoint.address ().to_string (), ":", oldEndpoint.port (),
" to ", newEndpoint.address ().to_string (), ":", newEndpoint.port ()); " to ", newEndpoint.address ().to_string (), ":", newEndpoint.port ());
} }
} }

28
SSU.h
View File

@ -23,11 +23,14 @@ namespace ssu
uint8_t iv[16]; uint8_t iv[16];
uint8_t flag; uint8_t flag;
uint32_t time; uint32_t time;
uint8_t GetPayloadType () const { return flag >> 4; };
}; };
#pragma pack() #pragma pack()
const int SSU_MTU = 1484; const size_t SSU_MTU = 1484;
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
// payload types (4 bits) // payload types (4 bits)
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0; const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
@ -37,7 +40,7 @@ namespace ssu
const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4; const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4;
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5; const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
const uint8_t PAYLOAD_TYPE_DATA = 6; const uint8_t PAYLOAD_TYPE_DATA = 6;
const uint8_t PAYLOAD_TYPE_TEST = 7; const uint8_t PAYLOAD_TYPE_PEER_TEST = 7;
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8; const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
// data flags // data flags
@ -70,7 +73,7 @@ namespace ssu
public: public:
SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
const i2p::data::RouterInfo * router = nullptr); const i2p::data::RouterInfo * router = nullptr, bool peerTest = false);
void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
~SSUSession (); ~SSUSession ();
@ -80,34 +83,43 @@ namespace ssu
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
const i2p::data::RouterInfo * GetRemoteRouter () const { return m_RemoteRouter; }; const i2p::data::RouterInfo * GetRemoteRouter () const { return m_RemoteRouter; };
void SendI2NPMessage (I2NPMessage * msg); void SendI2NPMessage (I2NPMessage * msg);
void SendPeerTest (); // Alice
private: private:
void CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey); void CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey);
void ProcessMessage (uint8_t * buf, size_t len); // call for established session void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
void ProcessIntroKeyMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for non-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 (const i2p::data::RouterInfo::Introducer& introducer); 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);
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag); void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress);
void ProcessRelayResponse (uint8_t * buf, size_t len); void ProcessRelayResponse (uint8_t * buf, size_t len);
void ProcessRelayIntro (uint8_t * buf, size_t len);
void Established (); void Established ();
void Failed (); void Failed ();
void HandleConnectTimer (const boost::system::error_code& ecode); void HandleConnectTimer (const boost::system::error_code& ecode);
void ProcessPeerTest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, uint8_t * introKey); // Charlie to Alice
void ProcessData (uint8_t * buf, size_t len); void ProcessData (uint8_t * buf, size_t len);
void SendMsgAck (uint32_t msgID); void SendMsgAck (uint32_t msgID);
void SendSesionDestroyed (); void SendSesionDestroyed ();
void Send (i2p::I2NPMessage * msg); void Send (i2p::I2NPMessage * msg);
void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key
bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey); void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey);
void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey); void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey);
bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey); bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey);
const uint8_t * GetIntroKey () const; const uint8_t * GetIntroKey () const;
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
private: private:
SSUServer& m_Server; SSUServer& m_Server;
@ -115,7 +127,9 @@ namespace ssu
const i2p::data::RouterInfo * m_RemoteRouter; const i2p::data::RouterInfo * m_RemoteRouter;
boost::asio::deadline_timer m_Timer; boost::asio::deadline_timer m_Timer;
i2p::data::DHKeysPair * m_DHKeysPair; // X - for client and Y - for server i2p::data::DHKeysPair * m_DHKeysPair; // X - for client and Y - for server
bool m_PeerTest;
SessionState m_State; SessionState m_State;
uint32_t m_RelayTag;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption; CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption;
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption; CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption m_Decryption;
uint8_t m_SessionKey[32], m_MacKey[32]; uint8_t m_SessionKey[32], m_MacKey[32];
@ -131,7 +145,7 @@ namespace ssu
~SSUServer (); ~SSUServer ();
void Start (); void Start ();
void Stop (); void Stop ();
SSUSession * GetSession (const i2p::data::RouterInfo * router); SSUSession * GetSession (const i2p::data::RouterInfo * router, bool peerTest = false);
SSUSession * FindSession (const i2p::data::RouterInfo * router); SSUSession * FindSession (const i2p::data::RouterInfo * router);
void DeleteSession (SSUSession * session); void DeleteSession (SSUSession * session);
void DeleteAllSessions (); void DeleteAllSessions ();

View File

@ -345,7 +345,7 @@ namespace stream
StreamingDestination::StreamingDestination (): m_LeaseSet (nullptr) StreamingDestination::StreamingDestination (): m_LeaseSet (nullptr)
{ {
m_Keys = i2p::data::CreateRandomKeys (); m_Keys = i2p::data::CreateRandomKeys ();
m_IdentHash = i2p::data::CalculateIdentHash (m_Keys.pub); m_IdentHash = m_Keys.pub.Hash ();
m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag,
CryptoPP::Integer (m_Keys.signingPrivateKey, 20)); CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);

View File

@ -92,7 +92,6 @@ namespace i2p
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));
m_Timer = new boost::asio::deadline_timer (m_Service);
// create acceptors // create acceptors
auto addresses = context.GetRouterInfo ().GetAddresses (); auto addresses = context.GetRouterInfo ().GetAddresses ();
for (auto& address : addresses) for (auto& address : addresses)
@ -124,23 +123,17 @@ namespace i2p
void Transports::Stop () void Transports::Stop ()
{ {
for (auto session: m_NTCPSessions)
delete session.second;
m_NTCPSessions.clear ();
delete m_NTCPAcceptor;
if (m_Timer)
{
m_Timer->cancel ();
delete m_Timer;
}
if (m_SSUServer) if (m_SSUServer)
{ {
m_SSUServer->Stop (); m_SSUServer->Stop ();
delete m_SSUServer; delete m_SSUServer;
} }
for (auto session: m_NTCPSessions)
delete session.second;
m_NTCPSessions.clear ();
delete m_NTCPAcceptor;
m_DHKeysPairSupplier.Stop (); m_DHKeysPairSupplier.Stop ();
m_IsRunning = false; m_IsRunning = false;
m_Service.stop (); m_Service.stop ();
@ -270,24 +263,10 @@ namespace i2p
{ {
auto router = i2p::data::netdb.GetRandomRouter (); auto router = i2p::data::netdb.GetRandomRouter ();
if (router && router->IsSSU () && m_SSUServer) if (router && router->IsSSU () && m_SSUServer)
m_SSUServer->GetSession (router); m_SSUServer->GetSession (router, true); // peer test
}
if (m_Timer)
{
m_Timer->expires_from_now (boost::posix_time::seconds(5)); // 5 seconds
m_Timer->async_wait (boost::bind (&Transports::HandleTimer, this, boost::asio::placeholders::error));
} }
} }
void Transports::HandleTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
// end of external IP detection
if (m_SSUServer)
m_SSUServer->DeleteAllSessions ();
}
}
i2p::data::DHKeysPair * Transports::GetNextDHKeysPair () i2p::data::DHKeysPair * Transports::GetNextDHKeysPair ()
{ {

View File

@ -71,7 +71,6 @@ namespace i2p
void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg); void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
void DetectExternalIP (); void DetectExternalIP ();
void HandleTimer (const boost::system::error_code& ecode);
private: private:
@ -83,7 +82,6 @@ namespace i2p
std::map<i2p::data::IdentHash, i2p::ntcp::NTCPSession *> m_NTCPSessions; std::map<i2p::data::IdentHash, i2p::ntcp::NTCPSession *> m_NTCPSessions;
i2p::ssu::SSUServer * m_SSUServer; i2p::ssu::SSUServer * m_SSUServer;
boost::asio::deadline_timer * m_Timer;
DHKeysPairSupplier m_DHKeysPairSupplier; DHKeysPairSupplier m_DHKeysPairSupplier;

View File

@ -169,14 +169,14 @@ namespace tunnel
*m_OutboundTunnels.begin () : tunnels.GetNextOutboundTunnel (); *m_OutboundTunnels.begin () : tunnels.GetNextOutboundTunnel ();
LogPrint ("Creating destination inbound tunnel..."); LogPrint ("Creating destination inbound tunnel...");
auto firstHop = i2p::data::netdb.GetRandomRouter (outboundTunnel ? outboundTunnel->GetEndpointRouter () : nullptr); auto firstHop = i2p::data::netdb.GetRandomRouter (outboundTunnel ? outboundTunnel->GetEndpointRouter () : nullptr);
auto secondHop = i2p::data::netdb.GetRandomRouter (firstHop); auto secondHop = outboundTunnel ? outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router : nullptr;
if (!secondHop || secondHop->GetIdentHash () == i2p::context.GetIdentHash ())
secondHop = i2p::data::netdb.GetRandomRouter (firstHop);
auto * tunnel = tunnels.CreateTunnel<InboundTunnel> ( auto * tunnel = tunnels.CreateTunnel<InboundTunnel> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *> new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{ {
firstHop, firstHop,
secondHop secondHop
// TODO: switch to 3-hops later
/*i2p::data::netdb.GetRandomRouter (secondHop) */
}), }),
outboundTunnel); outboundTunnel);
tunnel->SetTunnelPool (this); tunnel->SetTunnelPool (this);

View File

@ -249,6 +249,11 @@ namespace http
} }
} }
std::string url::portstr_ = "80";
unsigned int url::port_ = 80;
std::string url::user_ = "";
std::string url::pass_ = "";
url::url(const std::string& url_s) url::url(const std::string& url_s)
{ {
parse(url_s); parse(url_s);

8
util.h
View File

@ -41,10 +41,10 @@ namespace util
void parse(const std::string& url_s); void parse(const std::string& url_s);
public: public:
std::string protocol_, host_, path_, query_; std::string protocol_, host_, path_, query_;
std::string portstr_ = "80"; static std::string portstr_;
unsigned int port_ = 80; static unsigned int port_;
std::string user_ = ""; static std::string user_;
std::string pass_ = ""; static std::string pass_;
}; };
} }
} }