diff --git a/NTCPSession.cpp b/NTCPSession.cpp index ad71e3b0..7d53c541 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -3,7 +3,6 @@ #include "I2PEndian.h" #include #include -#include #include #include "base64.h" #include "Log.h" @@ -35,21 +34,36 @@ namespace ntcp void NTCPSession::CreateAESKey (uint8_t * pubKey, uint8_t * aesKey) { CryptoPP::DH dh (elgp, elgg); - CryptoPP::SecByteBlock secretKey(dh.AgreedValueLength()); - if (!dh.Agree (secretKey, m_DHKeysPair->privateKey, pubKey)) + uint8_t sharedKey[256]; + if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey)) { LogPrint ("Couldn't create shared key"); Terminate (); return; }; - if (secretKey[0] & 0x80) + if (sharedKey[0] & 0x80) { aesKey[0] = 0; - memcpy (aesKey + 1, secretKey, 31); + memcpy (aesKey + 1, sharedKey, 31); } - else - memcpy (aesKey, secretKey, 32); + else if (sharedKey[0]) + memcpy (aesKey, sharedKey, 32); + else + { + // find first non-zero byte + uint8_t * nonZero = sharedKey + 1; + while (!*nonZero) + { + nonZero++; + if (nonZero - sharedKey > 32) + { + LogPrint ("First 32 bytes of shared key is all zeros. Ignored"); + return; + } + } + memcpy (aesKey, nonZero, 32); + } } void NTCPSession::Terminate () diff --git a/RouterInfo.cpp b/RouterInfo.cpp index 9a4f8f97..da829c47 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -359,7 +359,8 @@ namespace data memcpy (addr.key, key, 32); m_Addresses.push_back(addr); m_SupportedTransports |= eSSUV4; - m_Caps |= eSSUTesting; // TODO + m_Caps |= eSSUTesting; + m_Caps |= eSSUIntroducer; } void RouterInfo::SetProperty (const char * key, const char * value) diff --git a/SSU.cpp b/SSU.cpp index 9946fa28..b32bfddb 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include "CryptoConst.h" #include "Log.h" #include "Timestamp.h" @@ -38,23 +38,40 @@ namespace ssu void SSUSession::CreateAESandMacKey (const uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey) { CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); - CryptoPP::SecByteBlock secretKey(dh.AgreedValueLength()); - if (!dh.Agree (secretKey, m_DHKeysPair->privateKey, pubKey)) + uint8_t sharedKey[256]; + if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey)) { LogPrint ("Couldn't create shared key"); return; }; - if (secretKey[0] & 0x80) + if (sharedKey[0] & 0x80) { aesKey[0] = 0; - memcpy (aesKey + 1, secretKey, 31); - memcpy (macKey, secretKey + 31, 32); + memcpy (aesKey + 1, sharedKey, 31); + memcpy (macKey, sharedKey + 31, 32); + } + else if (sharedKey[0]) + { + memcpy (aesKey, sharedKey, 32); + memcpy (macKey, sharedKey + 32, 32); } else { - memcpy (aesKey, secretKey, 32); - memcpy (macKey, secretKey + 32, 32); + // find first non-zero byte + uint8_t * nonZero = sharedKey + 1; + while (!*nonZero) + { + nonZero++; + if (nonZero - sharedKey > 32) + { + LogPrint ("First 32 bytes of shared key is all zeros. Ignored"); + return; + } + } + + memcpy (aesKey, nonZero, 32); + CryptoPP::SHA256().CalculateDigest(macKey, nonZero, 64 - (nonZero - sharedKey)); } m_IsSessionKey = true; } @@ -135,6 +152,10 @@ namespace ssu ProcessRelayResponse (buf, len); m_Server.DeleteSession (this); break; + case PAYLOAD_TYPE_RELAY_REQUEST: + LogPrint ("SSU relay request received"); + ProcessRelayRequest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); + break; case PAYLOAD_TYPE_RELAY_INTRO: LogPrint ("SSU relay intro received"); ProcessRelayIntro (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); @@ -277,6 +298,7 @@ namespace ssu LogPrint ("SSU is not supported"); return; } + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); uint8_t signedData[532]; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time memcpy (signedData, x, 256); // x @@ -294,8 +316,14 @@ namespace ssu memcpy (signedData + 512, payload - 6, 6); // remote endpoint IP and port *(uint32_t *)(signedData + 518) = htobe32 (address->host.to_v4 ().to_ulong ()); // our IP *(uint16_t *)(signedData + 522) = htobe16 (address->port); // our port - *(uint32_t *)(payload) = 0; // relay tag, always 0 for now - payload += 4; + uint32_t relayTag = 0; + if (i2p::context.GetRouterInfo ().IsIntroducer ()) + { + rnd.GenerateWord32 (relayTag); + m_Server.AddRelay (relayTag, m_RemoteEndpoint); + } + *(uint32_t *)(payload) = relayTag; + payload += 4; // relay tag *(uint32_t *)(payload) = htobe32 (i2p::util::GetSecondsSinceEpoch ()); // signed on time payload += 4; memcpy (signedData + 524, payload - 8, 8); // relayTag and signed on time @@ -303,7 +331,6 @@ namespace ssu // TODO: fill padding with random data uint8_t iv[16]; - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); rnd.GenerateBlock (iv, 16); // random iv // encrypt signature and 8 bytes padding with newly created session key m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); @@ -354,6 +381,82 @@ namespace ssu m_Server.Send (buf, 480, m_RemoteEndpoint); } + void SSUSession::ProcessRelayRequest (uint8_t * buf, size_t len) + { + uint32_t relayTag = be32toh (*(uint32_t *)buf); + auto session = m_Server.FindRelaySession (relayTag); + if (session) + { + buf += 4; // relay tag + uint8_t size = *buf; + if (size == 4) + { + buf++; // size + boost::asio::ip::address_v4 address (be32toh (*(uint32_t* )buf)); + buf += 4; // address + uint16_t port = be16toh (*(uint16_t *)buf); + buf += 2; // port + uint8_t challengeSize = *buf; + buf++; // challenge size + buf += challengeSize; + uint8_t * introKey = buf; + buf += 32; // introkey + uint32_t nonce = be32toh (*(uint32_t *)buf); + boost::asio::ip::udp::endpoint from (address, port); + SendRelayResponse (nonce, from, introKey, session->m_RemoteEndpoint); + SendRelayIntro (session, from); + } + else + LogPrint ("Address size ", size, " is not supported"); + } + } + + void SSUSession::SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from, const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to) + { + uint8_t buf[64 + 18]; + uint8_t * payload = buf + sizeof (SSUHeader); + // Charlie + *payload = 4; + payload++; // size + *(uint32_t *)payload = htobe32 (to.address ().to_v4 ().to_ulong ()); // Charlie's IP + payload += 4; // address + *(uint16_t *)payload = htobe16 (to.port ()); // Charlie's port + payload += 2; // port + // Alice + *payload = 4; + payload++; // size + *(uint32_t *)payload = htobe32 (from.address ().to_v4 ().to_ulong ()); // Alice's IP + payload += 4; // address + *(uint16_t *)payload = htobe16 (from.port ()); // Alice's port + payload += 2; // port + *(uint32_t *)payload = htobe32 (nonce); + + uint8_t iv[16]; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, 64, introKey, iv, introKey); + m_Server.Send (buf, 64, from); + } + + void SSUSession::SendRelayIntro (SSUSession * session, const boost::asio::ip::udp::endpoint& from) + { + if (!session) return; + uint8_t buf[48 + 18]; + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = 4; + payload++; // size + *(uint32_t *)payload = htobe32 (from.address ().to_v4 ().to_ulong ()); // Alice's IP + payload += 4; // address + *(uint16_t *)payload = htobe16 (from.port ()); // Alice's port + payload += 2; // port + *payload = 0; // challenge size + uint8_t iv[16]; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, 48, session->m_SessionKey, iv, session->m_MacKey); + m_Server.Send (buf, 48, session->m_RemoteEndpoint); + } + void SSUSession::ProcessRelayResponse (uint8_t * buf, size_t len) { LogPrint ("Relay response received"); @@ -912,6 +1015,19 @@ namespace ssu m_Socket.close (); } + void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay) + { + m_Relays[tag] = relay; + } + + SSUSession * SSUServer::FindRelaySession (uint32_t tag) + { + auto it = m_Relays.find (tag); + if (it != m_Relays.end ()) + return FindSession (it->second); + return nullptr; + } + void SSUServer::Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to) { m_Socket.send_to (boost::asio::buffer (buf, len), to); @@ -951,12 +1067,17 @@ namespace ssu if (!router) return nullptr; auto address = router->GetSSUAddress (); if (!address) return nullptr; - auto it = m_Sessions.find (boost::asio::ip::udp::endpoint (address->host, address->port)); + return FindSession (boost::asio::ip::udp::endpoint (address->host, address->port)); + } + + SSUSession * SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) + { + auto it = m_Sessions.find (e); if (it != m_Sessions.end ()) return it->second; else return nullptr; - } + } SSUSession * SSUServer::GetSession (const i2p::data::RouterInfo * router, bool peerTest) { diff --git a/SSU.h b/SSU.h index f648f5db..95198d21 100644 --- a/SSU.h +++ b/SSU.h @@ -99,6 +99,9 @@ namespace ssu void SendSessionCreated (const uint8_t * x); void ProcessSessionConfirmed (uint8_t * buf, size_t len); void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress); + void ProcessRelayRequest (uint8_t * buf, size_t len); + void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from, const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to); + void SendRelayIntro (SSUSession * session, const boost::asio::ip::udp::endpoint& from); void ProcessRelayResponse (uint8_t * buf, size_t len); void ProcessRelayIntro (uint8_t * buf, size_t len); void Established (); @@ -157,12 +160,15 @@ namespace ssu void Stop (); SSUSession * GetSession (const i2p::data::RouterInfo * router, bool peerTest = false); SSUSession * FindSession (const i2p::data::RouterInfo * router); + SSUSession * FindSession (const boost::asio::ip::udp::endpoint& e); void DeleteSession (SSUSession * session); void DeleteAllSessions (); boost::asio::io_service& GetService () { return m_Socket.get_io_service(); }; const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; }; void Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); + void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay); + SSUSession * FindRelaySession (uint32_t tag); private: @@ -176,6 +182,7 @@ namespace ssu boost::asio::ip::udp::endpoint m_SenderEndpoint; uint8_t m_ReceiveBuffer[2*SSU_MTU]; std::map m_Sessions; + std::map m_Relays; // we are introducer public: // for HTTP only diff --git a/Streaming.cpp b/Streaming.cpp index cb0cc285..1baffd46 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -160,7 +160,7 @@ namespace stream size += 4; // sendStreamID *(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID); size += 4; // receiveStreamID - *(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber); + *(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber++); size += 4; // sequenceNum *(uint32_t *)(packet + size) = 0; // TODO size += 4; // ack Through @@ -224,7 +224,7 @@ namespace stream size += 4; // sendStreamID *(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID); size += 4; // receiveStreamID - *(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber); + *(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber++); size += 4; // sequenceNum *(uint32_t *)(packet + size) = htobe32 (m_LastReceivedSequenceNumber); size += 4; // ack Through @@ -286,7 +286,7 @@ namespace stream } I2NPMessage * msg = i2p::garlic::routing.WrapMessage (m_RemoteLeaseSet, CreateDataMessage (this, buf, len), leaseSet); - if (!m_OutboundTunnel) + if (!m_OutboundTunnel || m_OutboundTunnel->IsFailed ()) m_OutboundTunnel = m_LocalDestination->GetTunnelPool ()->GetNextOutboundTunnel (); if (m_OutboundTunnel) {