From fb6ecdde1e982396fd02698bc84249b1558f9777 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 23 Mar 2022 21:48:41 -0400 Subject: [PATCH] handle TokenRequest --- libi2pd/SSU2.cpp | 126 +++++++++++++++++++++++++++++++++++++++++------ libi2pd/SSU2.h | 11 +++-- 2 files changed, 119 insertions(+), 18 deletions(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index fdaa5889..b7376453 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -60,6 +60,27 @@ namespace transport else SendTokenRequest (); } + + void SSU2Session::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) + { + // we are Bob + m_SourceConnID = connID; + Header header; + header.h.connID = connID; + memcpy (header.buf + 8, buf + 8, 8); + header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); + switch (header.h.type) + { + case eSSU2SessionRequest: + ProcessSessionRequest (header, buf, len); + break; + case eSSU2TokenRequest: + ProcessTokenRequest (header, buf, len); + break; + default: + LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type); + } + } void SSU2Session::SendSessionRequest (uint64_t token) { @@ -106,19 +127,9 @@ namespace transport m_Server.Send (header.buf, 16, headerX, 48, payload, payloadSize, m_RemoteEndpoint); } - void SSU2Session::ProcessSessionRequest (uint64_t connID, uint8_t * buf, size_t len) + void SSU2Session::ProcessSessionRequest (Header& header, uint8_t * buf, size_t len) { // we are Bob - m_SourceConnID = connID; - Header header; - header.h.connID = connID; - memcpy (header.buf + 8, buf + 8, 8); - header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); - if (header.h.type != eSSU2SessionRequest) - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type); - return; - } const uint8_t nonce[12] = {0}; uint8_t headerX[48]; i2p::crypto::ChaCha20 (buf + 16, 48, i2p::context.GetSSU2IntroKey (), nonce, headerX); @@ -405,6 +416,67 @@ namespace transport m_Server.AddPendingOutgoingSession (m_RemoteEndpoint, shared_from_this ()); m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); } + + void SSU2Session::ProcessTokenRequest (Header& header, uint8_t * buf, size_t len) + { + // we are Bob + const uint8_t nonce[12] = {0}; + uint8_t h[32]; + memcpy (h, header.buf, 16); + i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, h + 16); + memcpy (&m_DestConnID, h, 8); + // decrypt + uint8_t * payload = buf + 32; + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, + i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) + { + LogPrint (eLogWarning, "SSU2: TokenRequest AEAD verification failed "); + return; + } + // payload + HandlePayload (payload, len - 48); + SendRetry (); + } + + void SSU2Session::SendRetry () + { + // we are Bob + Header header; + uint8_t h[32], payload[64]; + // fill packet + header.h.connID = m_DestConnID; // dest id + header.h.packetNum = 0; + header.h.type = eSSU2Retry; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID + header.h.flags[2] = 0; // flag + memcpy (h, header.buf, 16); + memcpy (h + 16, &m_SourceConnID, 8); // source id + uint64_t token = m_Server.GetIncomingToken (m_RemoteEndpoint); + memcpy (h + 24, &token, 8); // token + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); + size_t payloadSize = 7; + payloadSize += CreateAddressBlock (m_RemoteEndpoint, payload, 57); + uint8_t paddingSize = rand () & 0x0F; // 0 - 15 + if (paddingSize) + { + payload[payloadSize] = eSSU2BlkPadding; + htobe16buf (payload + payloadSize + 1, paddingSize); + payloadSize += paddingSize + 3; + } + // encrypt + const uint8_t nonce[12] = {0}; + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, i2p::context.GetSSU2IntroKey (), nonce, payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 12)); + i2p::crypto::ChaCha20 (h + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, h + 16); + // send + m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); + } bool SSU2Session::ProcessRetry (uint8_t * buf, size_t len) { @@ -418,11 +490,24 @@ namespace transport LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type); return false; } - const uint8_t nonce[12] = {0}; + uint8_t nonce[12] = {0}; uint64_t headerX[2]; // sourceConnID, token i2p::crypto::ChaCha20 (buf + 16, 16, m_Address->i, nonce, (uint8_t *)headerX); m_Server.UpdateOutgoingToken (m_RemoteEndpoint, headerX[1], i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); - // TODO: decrypt and handle payload + // decrypt and handle payload + uint8_t * payload = buf + 32; + CreateNonce (be32toh (header.h.packetNum), nonce); + uint8_t h[32]; + memcpy (h, header.buf, 16); + memcpy (h + 16, &headerX, 16); + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, + m_Address->i, nonce, payload, len - 48, false)) + { + LogPrint (eLogWarning, "SSU2: Retry AEAD verification failed "); + return false; + } + HandlePayload (payload, len - 48); + InitNoiseXKState1 (*m_NoiseState, m_Address->s); // reset Noise TODO: check state SendSessionRequest (headerX[1]); return true; @@ -733,7 +818,7 @@ namespace transport // assume new incoming session auto session = std::make_shared (*this); session->SetRemoteEndpoint (senderEndpoint); - session->ProcessSessionRequest (connID, buf, len); + session->ProcessFirstIncomingMessage (connID, buf, len); } } } @@ -828,12 +913,23 @@ namespace transport m_OutgoingTokens[ep] = {token, exp}; } - uint64_t SSU2Server::FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep) + uint64_t SSU2Server::FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep) const { auto it = m_OutgoingTokens.find (ep); if (it != m_OutgoingTokens.end ()) return it->second.first; return 0; } + + uint64_t SSU2Server::GetIncomingToken (const boost::asio::ip::udp::endpoint& ep) + { + auto it = m_IncomingTokens.find (ep); + if (it != m_IncomingTokens.end ()) + return it->second.first; + uint64_t token; + RAND_bytes ((uint8_t *)&token, 8); + m_IncomingTokens.emplace (ep, std::make_pair (token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT)); + return token; + } } } diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 99cd23ff..ca6a50a2 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -95,18 +95,22 @@ namespace transport void Connect (); void Done () override {}; void SendI2NPMessages (const std::vector >& msgs) override {}; - - void ProcessSessionRequest (uint64_t connID, uint8_t * buf, size_t len); + + void ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len); bool ProcessSessionCreated (uint8_t * buf, size_t len); bool ProcessSessionConfirmed (uint8_t * buf, size_t len); bool ProcessRetry (uint8_t * buf, size_t len); private: + void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len); + void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len); + void SendSessionRequest (uint64_t token = 0); void SendSessionCreated (const uint8_t * X); void SendSessionConfirmed (const uint8_t * Y); void SendTokenRequest (); + void SendRetry (); void HandlePayload (const uint8_t * buf, size_t len); bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep); @@ -152,7 +156,8 @@ namespace transport std::shared_ptr address); void UpdateOutgoingToken (const boost::asio::ip::udp::endpoint& ep, uint64_t token, uint32_t exp); - uint64_t FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep); + uint64_t FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep) const; + uint64_t GetIncomingToken (const boost::asio::ip::udp::endpoint& ep); private: