From 4277fcc0c039880cb36a5e8a545209d4817c909e Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 26 Dec 2025 14:27:28 -0500 Subject: [PATCH] Post quantum SessionRequest message --- libi2pd/NTCP2.cpp | 279 +++++++++++++++++++++++++++++----------------- libi2pd/NTCP2.h | 39 ++++--- 2 files changed, 201 insertions(+), 117 deletions(-) diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 90b478af..a30b8257 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -33,6 +33,7 @@ namespace i2p namespace transport { NTCP2Establisher::NTCP2Establisher (): + m_CryptoType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD), m_SessionConfirmedBuffer (nullptr) { } @@ -42,9 +43,38 @@ namespace transport delete[] m_SessionConfirmedBuffer; } + void NTCP2Establisher::SetVersion (int version) + { +#if OPENSSL_PQ + switch (version) + { + case 3: + m_CryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD; + break; + case 4: + m_CryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD; + break; + case 5: + m_CryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD; + break; + default: + m_CryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; + } +#else + m_CryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; +#endif + } + bool NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub) { +#if OPENSSL_PQ + if (m_CryptoType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + i2p::crypto::InitNoiseXKState (*this, rs); + else + i2p::crypto::InitNoiseXKStateMLKEM (*this, m_CryptoType, rs); +#else i2p::crypto::InitNoiseXKState (*this, rs); +#endif // h = SHA256(h || epub) MixHash (epub, 32); // x25519 between pub and priv @@ -113,44 +143,84 @@ namespace transport bool NTCP2Establisher::CreateSessionRequestMessage (std::mt19937& rng) { - // create buffer and fill padding - auto paddingLength = rng () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes - m_SessionRequestBufferLen = paddingLength + 64; - RAND_bytes (m_SessionRequestBuffer + 64, paddingLength); + size_t offset = 0; // encrypt X i2p::crypto::CBCEncryption encryption; encryption.SetKey (m_RemoteIdentHash); +#if OPENSSL_PQ + if (m_CryptoType > i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + { + uint8_t pub[32]; + memcpy (pub, GetPub (), 32); + pub[31] |= 0x80; // set highest bit + encryption.Encrypt (pub, 32, m_IV, m_SessionRequestBuffer); // X + // ML-KEM encap_key + m_PQKeys = i2p::crypto::CreateMLKEMKeys (m_CryptoType); + m_PQKeys->GenerateKeys (); + } + else + encryption.Encrypt (GetPub (), 32, m_IV, m_SessionRequestBuffer); // X +#else encryption.Encrypt (GetPub (), 32, m_IV, m_SessionRequestBuffer); // X +#endif memcpy (m_IV, m_SessionRequestBuffer + 16, 16); // save last block as IV for SessionCreated + offset += 32; + // encryption key for next block if (!KDF1Alice ()) return false; + size_t maxMsgLength = NTCP2_SESSION_REQUEST_MAX_SIZE; +#if OPENSSL_PQ + if (m_PQKeys) + { + // ML-KEM frame + auto keyLen = i2p::crypto::GetMLKEMPublicKeyLen (m_CryptoType); + std::vector encapsKey(keyLen); + m_PQKeys->GetPublicKey (encapsKey.data ()); + // encrypt encapsKey + if (!Encrypt (encapsKey.data (), m_SessionRequestBuffer + offset, keyLen)) + { + LogPrint (eLogWarning, "NTCP2: SessionRequest ML-KEM encap_key frame AEAD encryption failed "); + return false; + } + MixHash (m_SessionRequestBuffer + offset, keyLen + 16); // h = SHA256(h || ciphertext) + offset += keyLen + 16; + maxMsgLength += keyLen + 16; + } +#endif + // calculate padding length + auto paddingLength = (offset + 32 < maxMsgLength) ? (rng () % (maxMsgLength - offset - 32)) : 0; // 32 bytes following options block size // fill options uint8_t options[32]; // actual options size is 16 bytes memset (options, 0, 16); options[0] = i2p::context.GetNetID (); // network ID options[1] = 2; // ver htobe16buf (options + 2, paddingLength); // padLen - // m3p2Len + // calculate m3p2Len auto riBuffer = i2p::context.CopyRouterInfoBuffer (); auto bufLen = riBuffer->GetBufferLen (); m3p2Len = bufLen + 4 + 16; // (RI header + RI + MAC for now) TODO: implement options htobe16buf (options + 4, m3p2Len); - // fill m3p2 payload (RouterInfo block) + // 2 bytes reserved + htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds + // 4 bytes reserved + // encrypt options + if (!Encrypt (options, m_SessionRequestBuffer + offset, 16)) + { + LogPrint (eLogWarning, "NTCP2: SessionRequest options frame AEAD encryption failed"); + return false; + } + offset += 32; + // padding + RAND_bytes (m_SessionRequestBuffer + offset, paddingLength); + m_SessionRequestBufferLen = offset + paddingLength; + // create m3p2 payload (RouterInfo block) for SessionConfirmed m_SessionConfirmedBuffer = new uint8_t[m3p2Len + 48]; // m3p1 is 48 bytes uint8_t * m3p2 = m_SessionConfirmedBuffer + 48; m3p2[0] = eNTCP2BlkRouterInfo; // block htobe16buf (m3p2 + 1, bufLen + 1); // flag + RI m3p2[3] = 0; // flag memcpy (m3p2 + 4, riBuffer->data (), bufLen); // TODO: eliminate extra copy - // 2 bytes reserved - htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds - // 4 bytes reserved - // encrypt options - if (!Encrypt (options, m_SessionRequestBuffer + 32, 16)) - { - LogPrint (eLogWarning, "NTCP2: SessionRequest failed to encrypt options"); - return false; - } + return true; } @@ -158,7 +228,7 @@ namespace transport { auto paddingLen = rng () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64); m_SessionCreatedBufferLen = paddingLen + 64; - RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen); + memset (m_SessionCreatedBuffer + 64, 0, paddingLen); // encrypt Y i2p::crypto::CBCEncryption encryption; encryption.SetKey (i2p::context.GetIdentHash ()); @@ -174,7 +244,7 @@ namespace transport { LogPrint (eLogWarning, "NTCP2: SessionCreated failed to encrypt options"); return false; - } + } return true; } @@ -191,8 +261,8 @@ namespace transport { LogPrint (eLogWarning, "NTCP2: SessionConfirmed failed to encrypt part1"); return false; - } - return true; + } + return true; } bool NTCP2Establisher::CreateSessionConfirmedMessagePart2 () @@ -207,7 +277,7 @@ namespace transport { LogPrint (eLogWarning, "NTCP2: SessionConfirmed failed to encrypt part2"); return false; - } + } // update h again MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext) return true; @@ -226,10 +296,10 @@ namespace transport { LogPrint (eLogWarning, "NTCP2: SessionRequest KDF failed"); return false; - } + } // verify MAC and decrypt options block (32 bytes) uint8_t options[16]; - if (Decrypt (m_SessionRequestBuffer + 32, options, 16)) + if (Decrypt (m_SessionRequestBuffer + 32, options, 16)) { // options if (options[0] && options[0] != i2p::context.GetNetID ()) @@ -283,10 +353,10 @@ namespace transport { LogPrint (eLogWarning, "NTCP2: SessionCreated KDF failed"); return false; - } + } // decrypt and verify MAC uint8_t payload[16]; - if (Decrypt (m_SessionCreatedBuffer + 32, payload, 16)) + if (Decrypt (m_SessionCreatedBuffer + 32, payload, 16)) { // options paddingLen = bufbe16toh(payload + 2); @@ -333,7 +403,7 @@ namespace transport { LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 KDF failed"); return false; - } + } if (Decrypt (m_SessionConfirmedBuffer + 48, m3p2Buf, m3p2Len - 16)) // calculate new h again for KDF data MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext) @@ -369,6 +439,9 @@ namespace transport memcpy (m_Establisher->m_RemoteStaticKey, addr->s, 32); memcpy (m_Establisher->m_IV, addr->i, 16); m_RemoteEndpoint = boost::asio::ip::tcp::endpoint (addr->host, addr->port); +#if OPENSSL_PQ + m_Establisher->SetVersion (addr->v); +#endif } else LogPrint (eLogWarning, "NTCP2: Missing NTCP2 address"); @@ -496,7 +569,7 @@ namespace transport LogPrint (eLogWarning, "NTCP2: Send SessionRequest KDF failed"); boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); return; - } + } // send message m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch (); boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionRequestBuffer, m_Establisher->m_SessionRequestBufferLen), boost::asio::transfer_all (), @@ -529,11 +602,11 @@ namespace transport else { m_Establisher->CreateEphemeralKey (); - boost::asio::post (m_Server.GetEstablisherService (), + boost::asio::post (m_Server.GetEstablisherService (), [s = shared_from_this (), bytes_transferred] () { s->ProcessSessionRequest (bytes_transferred);; - }); + }); } } @@ -568,8 +641,8 @@ namespace transport } else ReadSomethingAndTerminate (); // probing resistance - } - + } + void NTCP2Session::HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) @@ -579,12 +652,12 @@ namespace transport } else { - boost::asio::post (m_Server.GetEstablisherService (), + boost::asio::post (m_Server.GetEstablisherService (), [s = shared_from_this ()] () { s->SendSessionCreated (); - }); - } + }); + } } void NTCP2Session::SendSessionCreated () @@ -594,7 +667,7 @@ namespace transport LogPrint (eLogWarning, "NTCP2: Send SessionCreated KDF failed"); boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); return; - } + } // send message m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch (); boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionCreatedBuffer, m_Establisher->m_SessionCreatedBufferLen), boost::asio::transfer_all (), @@ -611,11 +684,11 @@ namespace transport else { m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval; - boost::asio::post (m_Server.GetEstablisherService (), + boost::asio::post (m_Server.GetEstablisherService (), [s = shared_from_this (), bytes_transferred] () { s->ProcessSessionCreated (bytes_transferred); - }); + }); } } @@ -646,9 +719,9 @@ namespace transport if (GetRemoteIdentity ()) i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); // assume wrong s key boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } - } - + } + } + void NTCP2Session::HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) @@ -659,27 +732,27 @@ namespace transport else { m_Establisher->m_SessionCreatedBufferLen += bytes_transferred; - boost::asio::post (m_Server.GetEstablisherService (), + boost::asio::post (m_Server.GetEstablisherService (), [s = shared_from_this ()] () { s->SendSessionConfirmed (); - }); + }); } } void NTCP2Session::SendSessionConfirmed () { - if (!m_Establisher->CreateSessionConfirmedMessagePart1 ()) + if (!m_Establisher->CreateSessionConfirmedMessagePart1 ()) { boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); return; - } + } if (!m_Establisher->CreateSessionConfirmedMessagePart2 ()) { LogPrint (eLogWarning, "NTCP2: Send SessionConfirmed Part2 KDF failed"); boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); return; - } + } // send message boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionConfirmedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); @@ -740,11 +813,11 @@ namespace transport else { m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval; - boost::asio::post (m_Server.GetEstablisherService (), + boost::asio::post (m_Server.GetEstablisherService (), [s = shared_from_this ()] () { s->ProcessSessionConfirmed ();; - }); + }); } } @@ -759,8 +832,8 @@ namespace transport auto buf = std::make_shared > (m_Establisher->m3p2Len - 16); // -MAC if (m_Establisher->ProcessSessionConfirmedMessagePart2 (buf->data ())) // TODO:handle in establisher thread { - // payload - // RI block must be first + // payload + // RI block must be first if ((*buf)[0] != eNTCP2BlkRouterInfo) { LogPrint (eLogWarning, "NTCP2: Unexpected block ", (int)(*buf)[0], " in SessionConfirmed"); @@ -774,7 +847,7 @@ namespace transport boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); return; } - boost::asio::post (m_Server.GetService (), + boost::asio::post (m_Server.GetService (), [s = shared_from_this (), buf, size] () { s->EstablishSessionAfterSessionConfirmed (buf, size); @@ -784,8 +857,8 @@ namespace transport boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); } else - boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } + boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); + } void NTCP2Session::EstablishSessionAfterSessionConfirmed (std::shared_ptr > buf, size_t size) { @@ -820,7 +893,7 @@ namespace transport LogPrint (eLogError, "NTCP2: RouterInfo is from future for ", (ri.GetTimestamp () - ts)/1000LL, " seconds"); SendTerminationAndTerminate (eNTCP2Message3Error); return; - } + } // update RouterInfo in netdb auto ri1 = i2p::data::netdb.AddRouterInfo (ri.GetBuffer (), ri.GetBufferLen ()); // ri1 points to one from netdb now if (!ri1) @@ -829,23 +902,23 @@ namespace transport Terminate (); return; } - + bool isOlder = false; if (ri.GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ()) - { + { // received RouterInfo is older than one in netdb isOlder = true; if (ri1->HasProfile ()) - { - auto profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile + { + auto profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile if (profile && profile->IsDuplicated ()) - { + { SendTerminationAndTerminate (eNTCP2Banned); return; - } - } + } + } } - + auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri1->GetNTCP2V4Address () : (i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri1->GetYggdrasilAddress () : ri1->GetNTCP2V6Address ()); if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32)) @@ -881,8 +954,8 @@ namespace transport } else Terminate (); - } - + } + void NTCP2Session::SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey) { #if OPENSSL_SIPHASH @@ -908,11 +981,11 @@ namespace transport void NTCP2Session::ClientLogin () { m_Establisher->CreateEphemeralKey (); - boost::asio::post (m_Server.GetEstablisherService (), + boost::asio::post (m_Server.GetEstablisherService (), [s = shared_from_this ()] () { s->SendSessionRequest (); - }); + }); } void NTCP2Session::ServerLogin () @@ -962,7 +1035,7 @@ namespace transport boost::system::error_code ec; size_t moreBytes = m_Socket.available(ec); if (!ec) - { + { if (moreBytes >= m_NextReceivedLen) { // read and process message immediately if available @@ -971,7 +1044,7 @@ namespace transport } else Receive (); - } + } else LogPrint (eLogWarning, "NTCP2: Socket error: ", ec.message ()); } @@ -999,8 +1072,8 @@ namespace transport { if (ecode) { - if (ecode != boost::asio::error::operation_aborted) - LogPrint (eLogWarning, "NTCP2: Receive read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + LogPrint (eLogWarning, "NTCP2: Receive read error: ", ecode.message ()); Terminate (); } else @@ -1061,9 +1134,9 @@ namespace transport break; case eNTCP2BlkRouterInfo: { - LogPrint (eLogDebug, "NTCP2: RouterInfo flag=", (int)frame[offset]); + LogPrint (eLogDebug, "NTCP2: RouterInfo flag=", (int)frame[offset]); if (size <= i2p::data::MAX_RI_BUFFER_SIZE + 1) - { + { auto newRi = i2p::data::netdb.AddRouterInfo (frame + offset + 1, size - 1); if (newRi) { @@ -1071,8 +1144,8 @@ namespace transport if (remoteIdentity && remoteIdentity->GetIdentHash () == newRi->GetIdentHash ()) // peer's RouterInfo update SetRemoteIdentity (newRi->GetIdentity ()); - } - } + } + } else LogPrint (eLogInfo, "NTCP2: RouterInfo block is too long ", size); break; @@ -1195,7 +1268,7 @@ namespace transport { LogPrint (eLogError, "NTCP2: Frame to send is too long ", totalLen); return; - } + } uint8_t nonce[12]; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; m_Server.AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers @@ -1225,7 +1298,7 @@ namespace transport LogPrint (eLogError, "NTCP2: Buffer to send is too long ", payloadLen); delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; return; - } + } // encrypt uint8_t nonce[12]; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; @@ -1283,7 +1356,7 @@ namespace transport if (msg) msg->Drop (); m_SendQueue.pop_front (); continue; - } + } size_t len = msg->GetNTCP2Length (); if (s + len + 3 <= NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) // 3 bytes block header { @@ -1319,20 +1392,20 @@ namespace transport m_SendQueue.clear (); if (!msgs.empty ()) other->SendI2NPMessages (msgs); - } - + } + size_t NTCP2Session::CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len) { if (len < 3) return 0; len -= 3; if (msgLen < 256) msgLen = 256; // for short message padding should not be always zero size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100; - if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) - { + if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) + { int l = (int)NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - msgLen -3; if (l <= 0) return 0; paddingSize = l; - } + } if (paddingSize > len) paddingSize = len; if (paddingSize) { @@ -1405,20 +1478,20 @@ namespace transport if (len > 0 && m_Establisher) boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, len), boost::asio::transfer_all (), [s = shared_from_this()](const boost::system::error_code& ecode, size_t bytes_transferred) - { + { s->Terminate (); }); else boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); - } - + } + void NTCP2Session::SendI2NPMessages (std::list >& msgs) { - if (m_IsTerminated || msgs.empty ()) + if (m_IsTerminated || msgs.empty ()) { msgs.clear (); return; - } + } bool empty = false; { std::lock_guard l(m_IntermediateQueueMutex); @@ -1435,20 +1508,20 @@ namespace transport std::list > msgs; { std::lock_guard l(m_IntermediateQueueMutex); - m_IntermediateQueue.swap (msgs); - } + m_IntermediateQueue.swap (msgs); + } bool isSemiFull = m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE/2; if (isSemiFull) - { + { for (auto it: msgs) if (it->onDrop) it->Drop (); // drop earlier because we can handle it else m_SendQueue.push_back (std::move (it)); - } + } else m_SendQueue.splice (m_SendQueue.end (), msgs); - + if (!m_IsSending && m_IsEstablished) SendQueue (); else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE) @@ -1470,8 +1543,8 @@ namespace transport { if (m_RemoteEndpoint.address ().is_v4 ()) return i2p::data::RouterInfo::eNTCP2V4; return i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? i2p::data::RouterInfo::eNTCP2V6Mesh : i2p::data::RouterInfo::eNTCP2V6; - } - + } + NTCP2Server::NTCP2Server (): RunnableServiceWithWork ("NTCP2"), m_TerminationTimer (GetService ()), m_ProxyType(eNoProxy), m_Resolver(GetService ()), @@ -1539,12 +1612,12 @@ namespace transport #if defined(__HAIKU__) LogPrint (eLogInfo, "NTCP2: Can't listen v6 TCP port ", address->port, ". IPV6_V6ONLY is not supported"); continue; // IPV6_V6ONLY is not supported. Don't listen ipv6 -#endif +#endif m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ())); try { - m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); - m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); + m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); + m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); #if defined(__linux__) && !defined(_NETINET_IN_H) if (!m_Address6 && !m_YggdrasilAddress) // only if not binded to address @@ -1645,7 +1718,7 @@ namespace transport auto it = m_NTCP2Sessions.find (session->GetRemoteIdentity ()->GetIdentHash ()); if (it != m_NTCP2Sessions.end () && it->second == session) m_NTCP2Sessions.erase (it); - } + } } std::shared_ptr NTCP2Server::FindNTCP2Session (const i2p::data::IdentHash& ident) @@ -1940,9 +2013,9 @@ namespace transport else { LogPrint(eLogError, "NTCP2: SOCKS proxy handshake error ", ec.message()); - conn->Terminate(); - } - }); + conn->Terminate(); + } + }); break; } case eHTTPProxy: @@ -2022,16 +2095,16 @@ namespace transport m_Address4 = addr; } - void NTCP2Server::AEADChaCha20Poly1305Encrypt (const std::vector >& bufs, + void NTCP2Server::AEADChaCha20Poly1305Encrypt (const std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac) { return m_Encryptor.Encrypt (bufs, key, nonce, mac); - } - + } + bool NTCP2Server::AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) { return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len); - } + } } } diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index b50d9087..dd3baa28 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -19,6 +19,7 @@ #include #include #include "Crypto.h" +#include "PostQuantum.h" #include "util.h" #include "RouterInfo.h" #include "TransportSession.h" @@ -30,7 +31,7 @@ namespace transport const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; const size_t NTCP2_SEND_AFTER_FRAME_SIZE = 16386; // send frame when exceeds this size - const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287; + const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287; // without ML-KEM frame const size_t NTCP2_SESSION_CREATED_MAX_SIZE = 287; const int NTCP2_MAX_PADDING_RATIO = 6; // in % @@ -87,6 +88,8 @@ namespace transport NTCP2Establisher (); ~NTCP2Establisher (); + void SetVersion (int version); + const uint8_t * GetPub () const { return m_EphemeralKeys->GetPublicKey (); }; const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set @@ -116,13 +119,21 @@ namespace transport bool ProcessSessionConfirmedMessagePart2 (uint8_t * m3p2Buf); std::shared_ptr m_EphemeralKeys; + i2p::data::CryptoKeyType m_CryptoType; uint8_t m_RemoteEphemeralPublicKey[32]; // x25519 uint8_t m_RemoteStaticKey[32], m_IV[16]; i2p::data::IdentHash m_RemoteIdentHash; uint16_t m3p2Len; +#if OPENSSL_PQ + std::unique_ptr m_PQKeys; + uint8_t m_SessionRequestBuffer[NTCP2_SESSION_REQUEST_MAX_SIZE + i2p::crypto::MLKEM1024_KEY_LENGTH + 16], + m_SessionCreatedBuffer[NTCP2_SESSION_CREATED_MAX_SIZE + i2p::crypto::MLKEM1024_KEY_LENGTH + 16], +#else uint8_t m_SessionRequestBuffer[NTCP2_SESSION_REQUEST_MAX_SIZE], - m_SessionCreatedBuffer[NTCP2_SESSION_CREATED_MAX_SIZE], * m_SessionConfirmedBuffer; + m_SessionCreatedBuffer[NTCP2_SESSION_CREATED_MAX_SIZE], +#endif + * m_SessionConfirmedBuffer; size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen; }; @@ -155,7 +166,7 @@ namespace transport void SendLocalRouterInfo (bool update) override; // after handshake or by update void SendI2NPMessages (std::list >& msgs) override; void MoveSendQueue (std::shared_ptr other); - + private: void Established (); @@ -182,7 +193,7 @@ namespace transport void HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void ProcessSessionConfirmed (); void EstablishSessionAfterSessionConfirmed (std::shared_ptr > buf, size_t size); - + // data void ReceiveLength (); void HandleReceivedLength (const boost::system::error_code& ecode, std::size_t bytes_transferred); @@ -234,10 +245,10 @@ namespace transport bool m_IsSending, m_IsReceiving; std::list > m_SendQueue; uint64_t m_NextRouterInfoResendTime; // seconds since epoch - + std::list > m_IntermediateQueue; // from transports mutable std::mutex m_IntermediateQueueMutex; - + uint16_t m_PaddingSizes[16]; int m_NextPaddingSize; }; @@ -255,7 +266,7 @@ namespace transport void Start () { StartIOService (); }; void Stop () { StopIOService (); }; }; - + public: enum ProxyType @@ -264,7 +275,7 @@ namespace transport eSocksProxy, eHTTPProxy }; - + NTCP2Server (); ~NTCP2Server (); @@ -273,11 +284,11 @@ namespace transport auto& GetService () { return GetIOService (); }; auto& GetEstablisherService () { return m_EstablisherService.GetService (); }; std::mt19937& GetRng () { return m_Rng; }; - void AEADChaCha20Poly1305Encrypt (const std::vector >& bufs, + void AEADChaCha20Poly1305Encrypt (const std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); bool AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); - + const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); + bool AddNTCP2Session (std::shared_ptr session, bool incoming = false); void RemoveNTCP2Session (std::shared_ptr session); @@ -298,7 +309,7 @@ namespace transport void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); - + // timer void ScheduleTermination (); void HandleTerminationTimer (const boost::system::error_code& ecode); @@ -315,13 +326,13 @@ namespace transport uint16_t m_ProxyPort; boost::asio::ip::tcp::resolver m_Resolver; std::unique_ptr m_ProxyEndpoint; - + std::shared_ptr m_Address4, m_Address6, m_YggdrasilAddress; std::mt19937 m_Rng; EstablisherService m_EstablisherService; i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor; i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor; - + public: // for HTTP/I2PControl