diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index d081e957..51ff30a4 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -193,7 +193,7 @@ namespace transport m_State = eSSU2SessionStateEstablished; m_EphemeralKeys = nullptr; m_NoiseState.reset (nullptr); - m_SessionConfirmedFragment1.reset (nullptr); + m_SessionConfirmedFragment.reset (nullptr); m_SentHandshakePacket.reset (nullptr); m_ConnectTimer.cancel (); SetTerminationTimeout (SSU2_TERMINATION_TIMEOUT); @@ -326,6 +326,10 @@ namespace transport m_Server.Send (m_SentHandshakePacket->header.buf, 16, m_SentHandshakePacket->headerX, 48, m_SentHandshakePacket->payload, m_SentHandshakePacket->payloadSize, m_RemoteEndpoint); m_SentHandshakePacket->nextResendTime = ts + SSU2_HANDSHAKE_RESEND_INTERVAL; + if (m_SessionConfirmedFragment && m_State == eSSU2SessionStateSessionConfirmedSent) + // resend second fragment of SessionConfirmed + m_Server.Send (m_SessionConfirmedFragment->header.buf, 16, + m_SessionConfirmedFragment->payload, m_SessionConfirmedFragment->payloadSize, m_RemoteEndpoint); return; } // resend data packets @@ -623,10 +627,17 @@ namespace transport memset (header.h.flags, 0, 3); header.h.flags[0] = 1; // frag, total fragments always 1 // payload - const size_t maxPayloadSize = SSU2_MAX_PAYLOAD_SIZE - 48; // part 2 + size_t maxPayloadSize = SSU2_MAX_PAYLOAD_SIZE - 64; // part 2 uint8_t * payload = m_SentHandshakePacket->payload; size_t payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ()); - // TODO: check is RouterInfo doesn't fit and split by two fragments + if (!payloadSize) + { + // split by two fragments + maxPayloadSize += SSU2_MAX_PAYLOAD_SIZE; + payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ()); + header.h.flags[0] = 0x02; // frag 0, total fragments 2 + // TODO: check if we need more fragments + } if (payloadSize < maxPayloadSize) payloadSize += CreatePaddingBlock (payload + payloadSize, maxPayloadSize - payloadSize); // KDF for Session Confirmed part 1 @@ -646,14 +657,42 @@ namespace transport i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, payload, payloadSize + 16, true); payloadSize += 16; m_NoiseState->MixHash (payload, payloadSize); // h = SHA256(h || ciphertext); + m_SentHandshakePacket->payloadSize = payloadSize; + if (header.h.flags[0] > 1) + { + if (payloadSize > SSU2_MAX_PAYLOAD_SIZE - 64) + { + payloadSize = SSU2_MAX_PAYLOAD_SIZE - 64 - (rand () % 16); + if (m_SentHandshakePacket->payloadSize - payloadSize < 24) + payloadSize -= 24; + } + else + header.h.flags[0] = 1; + } // Encrypt header header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); header.ll[1] ^= CreateHeaderMask (kh2, payload + (payloadSize - 12)); m_State = eSSU2SessionStateSessionConfirmedSent; - m_SentHandshakePacket->payloadSize = payloadSize; // send m_Server.Send (header.buf, 16, part1, 48, payload, payloadSize, m_RemoteEndpoint); m_SendPacketNum++; + if (m_SentHandshakePacket->payloadSize > payloadSize) + { + // send second fragment + m_SessionConfirmedFragment.reset (new HandshakePacket); + Header& header = m_SessionConfirmedFragment->header; + header.h.connID = m_DestConnID; // dest id + header.h.packetNum = 0; + header.h.type = eSSU2SessionConfirmed; + memset (header.h.flags, 0, 3); + header.h.flags[0] = 0x12; // frag 1, total fragments 2 + m_SessionConfirmedFragment->payloadSize = m_SentHandshakePacket->payloadSize - payloadSize; + memcpy (m_SessionConfirmedFragment->payload, m_SentHandshakePacket->payload + payloadSize, m_SessionConfirmedFragment->payloadSize); + m_SentHandshakePacket->payloadSize = payloadSize; + header.ll[0] ^= CreateHeaderMask (m_Address->i, m_SessionConfirmedFragment->payload + (m_SessionConfirmedFragment->payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (kh2, m_SessionConfirmedFragment->payload + (m_SessionConfirmedFragment->payloadSize - 12)); + m_Server.Send (header.buf, 16, m_SessionConfirmedFragment->payload, m_SessionConfirmedFragment->payloadSize, m_RemoteEndpoint); + } } bool SSU2Session::ProcessSessionConfirmed (uint8_t * buf, size_t len) @@ -677,24 +716,24 @@ namespace transport if (!(header.h.flags[0] & 0xF0)) { // first fragment - if (!m_SessionConfirmedFragment1) + if (!m_SessionConfirmedFragment) { - m_SessionConfirmedFragment1.reset (new HandshakePacket); - m_SessionConfirmedFragment1->header = header; - memcpy (m_SessionConfirmedFragment1->payload, buf + 16, len - 16); - m_SessionConfirmedFragment1->payloadSize = len - 16; + m_SessionConfirmedFragment.reset (new HandshakePacket); + m_SessionConfirmedFragment->header = header; + memcpy (m_SessionConfirmedFragment->payload, buf + 16, len - 16); + m_SessionConfirmedFragment->payloadSize = len - 16; } return true; // wait for second fragment } else { // second fragment - if (!m_SessionConfirmedFragment1) return false; // out of sequence - header = m_SessionConfirmedFragment1->header; - memcpy (m_SessionConfirmedFragment1->payload + m_SessionConfirmedFragment1->payloadSize, buf + 16, len - 16); - m_SessionConfirmedFragment1->payloadSize += (len - 16); - buf = m_SessionConfirmedFragment1->payload - 16; - len = m_SessionConfirmedFragment1->payloadSize + 16; + if (!m_SessionConfirmedFragment) return false; // out of sequence + header = m_SessionConfirmedFragment->header; + memcpy (m_SessionConfirmedFragment->payload + m_SessionConfirmedFragment->payloadSize, buf + 16, len - 16); + m_SessionConfirmedFragment->payloadSize += (len - 16); + buf = m_SessionConfirmedFragment->payload - 16; + len = m_SessionConfirmedFragment->payloadSize + 16; } } // KDF for Session Confirmed part 1 @@ -725,7 +764,6 @@ namespace transport return false; } m_NoiseState->MixHash (payload, len - 64); // h = SHA256(h || ciphertext); - if (m_SessionConfirmedFragment1) m_SessionConfirmedFragment1.reset (nullptr); // payload // handle RouterInfo block that must be first if (decryptedPayload[0] != eSSU2BlkRouterInfo) diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index e9adfe8e..a8e0309c 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -302,8 +302,8 @@ namespace transport SSU2Server& m_Server; std::shared_ptr m_EphemeralKeys; std::unique_ptr m_NoiseState; - std::unique_ptr m_SessionConfirmedFragment1; // for Bob if applicable - std::unique_ptr m_SentHandshakePacket; // SessionRequest or SessionCreated + std::unique_ptr m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice + std::unique_ptr m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed std::shared_ptr m_Address; boost::asio::ip::udp::endpoint m_RemoteEndpoint; i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports; // for peer tests