Browse Source

Merge pull request #28 from orignal/master

Merge pull request from orignal/master
pull/63/head
chertov 10 years ago
parent
commit
b6b9c61217
  1. 9
      AddressBook.cpp
  2. 21
      Identity.cpp
  3. 8
      Identity.h
  4. 2
      LeaseSet.cpp
  5. 4
      NTCPSession.cpp
  6. 2
      NTCPSession.h
  7. 19
      RouterInfo.cpp
  8. 6
      RouterInfo.h
  9. 366
      SSU.cpp
  10. 34
      SSU.h
  11. 2
      Streaming.cpp
  12. 35
      Transports.cpp
  13. 2
      Transports.h
  14. 6
      TunnelPool.cpp
  15. 5
      util.cpp
  16. 8
      util.h

9
AddressBook.cpp

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

21
Identity.cpp

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
#include <cryptopp/dsa.h>
#include "CryptoConst.h"
#include "Identity.h"
#include "base64.h"
namespace i2p
{
@ -17,6 +18,19 @@ namespace data @@ -17,6 +18,19 @@ namespace data
memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey));
memset (certificate, 0, sizeof (certificate));
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)
@ -26,13 +40,6 @@ namespace data @@ -26,13 +40,6 @@ namespace data
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 keys;

8
Identity.h

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

2
LeaseSet.cpp

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

4
NTCPSession.cpp

@ -519,7 +519,7 @@ namespace ntcp @@ -519,7 +519,7 @@ namespace ntcp
void NTCPSession::ScheduleTermination ()
{
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,
this, boost::asio::placeholders::error));
}
@ -528,7 +528,7 @@ namespace ntcp @@ -528,7 +528,7 @@ namespace ntcp
{
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 ();
}
}

2
NTCPSession.h

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

19
RouterInfo.cpp

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

6
RouterInfo.h

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

366
SSU.cpp

@ -16,8 +16,10 @@ namespace ssu @@ -16,8 +16,10 @@ namespace ssu
{
SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
const i2p::data::RouterInfo * router): m_Server (server), m_RemoteEndpoint (remoteEndpoint),
m_RemoteRouter (router), m_Timer (m_Server.GetService ()), m_State (eSessionStateUnknown)
const i2p::data::RouterInfo * router, bool peerTest ):
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 ();
}
@ -57,16 +59,14 @@ namespace ssu @@ -57,16 +59,14 @@ namespace ssu
case eSessionStateConfirmedSent:
case eSessionStateEstablished:
// most common case
ProcessMessage (buf, len);
ScheduleTermination ();
ProcessMessage (buf, len, senderEndpoint);
break;
// establishing
// establishing or testing
case eSessionStateUnknown:
// session request
ProcessSessionRequest (buf, len, senderEndpoint);
break;
case eSessionStateRequestSent:
// session created
ProcessSessionCreated (buf, len);
// we must use intro key
ProcessIntroKeyMessage (buf, len, senderEndpoint);
break;
case eSessionStateCreatedSent:
// session confirmed
@ -92,21 +92,21 @@ namespace ssu @@ -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))
{
Decrypt (buf, len, m_SessionKey);
SSUHeader * header = (SSUHeader *)buf;
uint8_t payloadType = header->flag >> 4;
switch (payloadType)
switch (header->GetPayloadType ())
{
case PAYLOAD_TYPE_DATA:
LogPrint ("SSU data received");
ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader));
break;
case PAYLOAD_TYPE_TEST:
LogPrint ("SSU test received");
case PAYLOAD_TYPE_PEER_TEST:
LogPrint ("SSU peer test received");
ProcessPeerTest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint);
break;
case PAYLOAD_TYPE_SESSION_DESTROYED:
{
@ -116,10 +116,10 @@ namespace ssu @@ -116,10 +116,10 @@ namespace ssu
}
case PAYLOAD_TYPE_RELAY_INTRO:
LogPrint ("SSU relay intro received");
// TODO:
ProcessRelayIntro (buf + sizeof (SSUHeader), len - sizeof (SSUHeader));
break;
default:
LogPrint ("Unexpected SSU payload type ", (int)payloadType);
LogPrint ("Unexpected SSU payload type ", (int)header->GetPayloadType ());
}
}
else
@ -139,67 +139,92 @@ namespace ssu @@ -139,67 +139,92 @@ 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");
// use our intro key
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, buf, len))
auto introKey = GetIntroKey ();
if (!introKey)
{
m_State = eSessionStateRequestReceived;
LogPrint ("Session request received");
m_RemoteEndpoint = senderEndpoint;
SendSessionCreated (buf + sizeof (SSUHeader));
}
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;
LogPrint ("Session request received");
m_RemoteEndpoint = senderEndpoint;
SendSessionCreated (buf + sizeof (SSUHeader));
}
void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len)
{
LogPrint ("Process session created");
if (!m_RemoteRouter)
{
LogPrint ("Unsolicited session created message");
return;
}
// use remote intro key
if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, buf, len))
{
m_State = eSessionStateCreatedReceived;
LogPrint ("Session created received");
m_Timer.cancel (); // connect timer
uint8_t signedData[532]; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time
uint8_t * payload = buf + sizeof (SSUHeader);
uint8_t * y = payload;
memcpy (signedData, m_DHKeysPair->publicKey, 256); // x
memcpy (signedData + 256, y, 256); // y
payload += 256;
payload += 1; // size, assume 4
uint8_t * ourAddress = payload;
boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )ourAddress));
payload += 4; // address
uint16_t ourPort = be16toh (*(uint16_t *)payload);
payload += 2; // port
memcpy (signedData + 512, ourAddress, 6); // our IP and port
LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP.to_string ().c_str ());
*(uint32_t *)(signedData + 518) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ()); // remote IP
*(uint16_t *)(signedData + 522) = htobe16 (m_RemoteEndpoint.port ()); // remote port
memcpy (signedData + 524, payload, 8); // relayTag and signed on time
uint32_t relayTag = be32toh (*(uint32_t *)payload);
payload += 4; // relayTag
payload += 4; // signed on time
// decrypt DSA signature
m_Decryption.SetKeyWithIV (m_SessionKey, 32, ((SSUHeader *)buf)->iv);
m_Decryption.ProcessData (payload, payload, 48);
// verify
CryptoPP::DSA::PublicKey pubKey;
pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, CryptoPP::Integer (m_RemoteRouter->GetRouterIdentity ().signingKey, 128));
CryptoPP::DSA::Verifier verifier (pubKey);
if (!verifier.VerifyMessage (signedData, 532, payload, 40))
LogPrint ("SSU signature verification failed");
SendSessionConfirmed (y, ourAddress, relayTag);
}
m_State = eSessionStateCreatedReceived;
LogPrint ("Session created received");
m_Timer.cancel (); // connect timer
uint8_t signedData[532]; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time
uint8_t * payload = buf + sizeof (SSUHeader);
uint8_t * y = payload;
memcpy (signedData, m_DHKeysPair->publicKey, 256); // x
memcpy (signedData + 256, y, 256); // y
payload += 256;
payload += 1; // size, assume 4
uint8_t * ourAddress = payload;
boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )ourAddress));
payload += 4; // address
uint16_t ourPort = be16toh (*(uint16_t *)payload);
payload += 2; // port
memcpy (signedData + 512, ourAddress, 6); // our IP and port
LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort);
i2p::context.UpdateAddress (ourIP.to_string ().c_str ());
*(uint32_t *)(signedData + 518) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ()); // remote IP
*(uint16_t *)(signedData + 522) = htobe16 (m_RemoteEndpoint.port ()); // remote port
memcpy (signedData + 524, payload, 8); // relayTag and signed on time
m_RelayTag = be32toh (*(uint32_t *)payload);
payload += 4; // relayTag
payload += 4; // signed on time
// decrypt DSA signature
m_Decryption.SetKeyWithIV (m_SessionKey, 32, ((SSUHeader *)buf)->iv);
m_Decryption.ProcessData (payload, payload, 48);
// verify
CryptoPP::DSA::PublicKey pubKey;
pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, CryptoPP::Integer (m_RemoteRouter->GetRouterIdentity ().signingKey, 128));
CryptoPP::DSA::Verifier verifier (pubKey);
if (!verifier.VerifyMessage (signedData, 532, payload, 40))
LogPrint ("SSU signature verification failed");
SendSessionConfirmed (y, ourAddress);
}
void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len)
@ -209,7 +234,7 @@ namespace ssu @@ -209,7 +234,7 @@ namespace ssu
{
Decrypt (buf, len, m_SessionKey);
SSUHeader * header = (SSUHeader *)buf;
if ((header->flag >> 4) == PAYLOAD_TYPE_SESSION_CONFIRMED)
if (header->GetPayloadType () == PAYLOAD_TYPE_SESSION_CONFIRMED)
{
m_State = eSessionStateConfirmedReceived;
LogPrint ("Session confirmed received");
@ -326,7 +351,7 @@ namespace ssu @@ -326,7 +351,7 @@ namespace ssu
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 * payload = buf + sizeof (SSUHeader);
@ -352,7 +377,7 @@ namespace ssu @@ -352,7 +377,7 @@ namespace ssu
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
*(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
i2p::context.Sign (signedData, 532, payload); // DSA signature
@ -406,34 +431,21 @@ namespace ssu @@ -406,34 +431,21 @@ 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 ();
if (introKey)
uint8_t size = *buf;
if (size == 4)
{
// use intro key for verification and decryption
if (Validate (buf, len, introKey))
{
Decrypt (buf, len, introKey);
SSUHeader * header = (SSUHeader *)buf;
if ((header->flag >> 4) == expectedPayloadType)
{
CreateAESandMacKey (buf + sizeof (SSUHeader), m_SessionKey, m_MacKey);
return true;
}
else
LogPrint ("Unexpected payload type ", (int)(header->flag >> 4));
}
else
{
LogPrint ("MAC verification failed");
Failed ();
}
buf++; // size
boost::asio::ip::address_v4 address (be32toh (*(uint32_t* )buf));
buf += 4; // address
uint16_t port = be16toh (*(uint16_t *)buf);
// send hole punch of 1 byte
m_Server.Send (buf, 1, boost::asio::ip::udp::endpoint (address, port));
}
else
LogPrint ("SSU is not supported");
return false;
}
LogPrint ("Address size ", size, " is not supported");
}
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len,
const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey)
@ -543,7 +555,10 @@ namespace ssu @@ -543,7 +555,10 @@ namespace ssu
for (auto it :m_DelayedMessages)
Send (it);
m_DelayedMessages.clear ();
}
}
if (m_PeerTest)
SendPeerTest ();
ScheduleTermination ();
}
void SSUSession::Failed ()
@ -555,7 +570,24 @@ namespace ssu @@ -555,7 +570,24 @@ namespace ssu
m_Server.DeleteSession (this); // delete this
}
}
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
{
if (m_RemoteRouter)
@ -678,6 +710,87 @@ namespace ssu @@ -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)
{
uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16
@ -704,21 +817,11 @@ namespace ssu @@ -704,21 +817,11 @@ namespace ssu
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
rnd.GenerateBlock (iv, 16); // random iv
if (m_State == eSessionStateEstablished)
{
// encrypt message with session key
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)
@ -769,8 +872,26 @@ namespace ssu @@ -769,8 +872,26 @@ namespace ssu
len = 0;
fragmentNum++;
}
}
}
void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len)
{
uint8_t buf[SSU_MTU + 18];
uint8_t iv[16];
size_t msgSize = len + sizeof (SSUHeader);
if (msgSize > SSU_MTU)
{
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):
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (service, m_Endpoint)
{
@ -841,7 +962,7 @@ namespace ssu @@ -841,7 +962,7 @@ namespace ssu
return nullptr;
}
SSUSession * SSUServer::GetSession (const i2p::data::RouterInfo * router)
SSUSession * SSUServer::GetSession (const i2p::data::RouterInfo * router, bool peerTest)
{
SSUSession * session = nullptr;
if (router)
@ -859,10 +980,10 @@ namespace ssu @@ -859,10 +980,10 @@ namespace ssu
if (!router->UsesIntroducer ())
{
// connect directly
session = new SSUSession (*this, remoteEndpoint, router);
session = new SSUSession (*this, remoteEndpoint, router, peerTest);
m_Sessions[remoteEndpoint] = session;
LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port (), " created");
LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
session->Connect ();
}
else
@ -872,11 +993,20 @@ namespace ssu @@ -872,11 +993,20 @@ namespace ssu
{
auto& introducer = address->introducers[0]; // TODO:
boost::asio::ip::udp::endpoint introducerEndpoint (introducer.iHost, introducer.iPort);
session = new SSUSession (*this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = session;
LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (),
"] through introducer ", introducerEndpoint.address ().to_string (), ":", introducerEndpoint.port ());
session->ConnectThroughIntroducer (introducer);
it = m_Sessions.find (introducerEndpoint);
if (it == m_Sessions.end ())
{
session = new SSUSession (*this, introducerEndpoint, router);
m_Sessions[introducerEndpoint] = session;
LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (),
"] through introducer ", introducerEndpoint.address ().to_string (), ":", introducerEndpoint.port ());
session->ConnectThroughIntroducer (introducer);
}
else
{
LogPrint ("Session to introducer already exists");
// TODO:
}
}
else
LogPrint ("Router is unreachable, but not introducers presentd. Ignored");
@ -917,7 +1047,7 @@ namespace ssu @@ -917,7 +1047,7 @@ namespace ssu
auto session = it->second;
m_Sessions.erase (it);
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 ());
}
}

34
SSU.h

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

2
Streaming.cpp

@ -345,7 +345,7 @@ namespace stream @@ -345,7 +345,7 @@ namespace stream
StreamingDestination::StreamingDestination (): m_LeaseSet (nullptr)
{
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,
CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);

35
Transports.cpp

@ -92,7 +92,6 @@ namespace i2p @@ -92,7 +92,6 @@ namespace i2p
m_DHKeysPairSupplier.Start ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this));
m_Timer = new boost::asio::deadline_timer (m_Service);
// create acceptors
auto addresses = context.GetRouterInfo ().GetAddresses ();
for (auto& address : addresses)
@ -124,22 +123,16 @@ namespace i2p @@ -124,22 +123,16 @@ namespace i2p
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)
{
m_SSUServer->Stop ();
delete m_SSUServer;
}
}
for (auto session: m_NTCPSessions)
delete session.second;
m_NTCPSessions.clear ();
delete m_NTCPAcceptor;
m_DHKeysPairSupplier.Stop ();
m_IsRunning = false;
@ -270,24 +263,10 @@ namespace i2p @@ -270,24 +263,10 @@ namespace i2p
{
auto router = i2p::data::netdb.GetRandomRouter ();
if (router && router->IsSSU () && m_SSUServer)
m_SSUServer->GetSession (router);
}
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));
m_SSUServer->GetSession (router, true); // peer test
}
}
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 ()
{

2
Transports.h

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

6
TunnelPool.cpp

@ -169,14 +169,14 @@ namespace tunnel @@ -169,14 +169,14 @@ namespace tunnel
*m_OutboundTunnels.begin () : tunnels.GetNextOutboundTunnel ();
LogPrint ("Creating destination inbound tunnel...");
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> (
new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
{
firstHop,
secondHop
// TODO: switch to 3-hops later
/*i2p::data::netdb.GetRandomRouter (secondHop) */
}),
outboundTunnel);
tunnel->SetTunnelPool (this);

5
util.cpp

@ -249,6 +249,11 @@ namespace http @@ -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)
{
parse(url_s);

8
util.h

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

Loading…
Cancel
Save