diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index f8fb9946..7e320888 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -8,6 +8,7 @@ #include #include "TunnelBase.h" #include +#include "Crypto.h" #if LEGACY_OPENSSL #include "ChaCha20.h" #include "Poly1305.h" @@ -16,7 +17,6 @@ #endif #include "I2PEndian.h" #include "Log.h" -#include "Crypto.h" namespace i2p { diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 71af855c..187f4435 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -66,7 +66,7 @@ namespace transport HMAC(EVP_sha256(), tempKey, 32, m_CK, 33, derived, &len); } - void NTCP2Session::KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * pub, uint8_t * derived) + void NTCP2Session::KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * priv, const uint8_t * pub, uint8_t * derived) { static const char protocolName[] = "Noise_XK_25519_ChaChaPoly_SHA256"; // 32 bytes uint8_t h[64]; @@ -81,12 +81,12 @@ namespace transport // x25519 between rs and priv uint8_t inputKeyMaterial[32]; BN_CTX * ctx = BN_CTX_new (); - i2p::crypto::GetEd25519 ()->ScalarMul (rs, m_ExpandedPrivateKey, inputKeyMaterial, ctx); // rs*priv + i2p::crypto::GetEd25519 ()->ScalarMul (rs, priv, inputKeyMaterial, ctx); // rs*priv BN_CTX_free (ctx); MixKey (inputKeyMaterial, derived); } - void NTCP2Session::KeyDerivationFunction2 (const uint8_t * pub, const uint8_t * sessionRequest, size_t sessionRequestLen, uint8_t * derived) + void NTCP2Session::KeyDerivationFunction2 (const uint8_t * priv, const uint8_t * pub, const uint8_t * sessionRequest, size_t sessionRequestLen, uint8_t * derived) { uint8_t h[64]; memcpy (h, m_H, 32); @@ -106,7 +106,7 @@ namespace transport // x25519 between remote pub and priv uint8_t inputKeyMaterial[32]; BN_CTX * ctx = BN_CTX_new (); - i2p::crypto::GetEd25519 ()->ScalarMul (pub, m_ExpandedPrivateKey, inputKeyMaterial, ctx); + i2p::crypto::GetEd25519 ()->ScalarMul (pub, priv, inputKeyMaterial, ctx); BN_CTX_free (ctx); MixKey (inputKeyMaterial, derived); } @@ -122,11 +122,9 @@ namespace transport void NTCP2Session::CreateEphemeralKey (uint8_t * pub) { - uint8_t key[32]; - RAND_bytes (key, 32); - i2p::crypto::Ed25519::ExpandPrivateKey (key, m_ExpandedPrivateKey); + RAND_bytes (m_EphemeralPrivateKey, 32); BN_CTX * ctx = BN_CTX_new (); - i2p::crypto::GetEd25519 ()->ScalarMulB (m_ExpandedPrivateKey, pub, ctx); + i2p::crypto::GetEd25519 ()->ScalarMulB (m_EphemeralPrivateKey, pub, ctx); BN_CTX_free (ctx); } @@ -148,7 +146,7 @@ namespace transport encryption.GetIV (m_IV); // save IV for SessionCreated // encryption key for next block uint8_t key[32]; - KeyDerivationFunction1 (m_RemoteStaticKey, x, key); + KeyDerivationFunction1 (m_RemoteStaticKey, m_EphemeralPrivateKey, x, key); // fill options uint8_t options[32]; // actual options size is 16 bytes memset (options, 0, 16); @@ -184,6 +182,96 @@ namespace transport } } + void NTCP2Session::HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + (void) bytes_transferred; + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: SessionRequest read error: ", ecode.message ()); + Terminate (); + } + else + { + // decrypt X + i2p::crypto::CBCDecryption decryption; + decryption.SetKey (i2p::context.GetIdentHash ()); + decryption.SetIV (i2p::context.GetNTCP2IV ()); + decryption.Decrypt (m_SessionRequestBuffer, 32, m_Y); + decryption.GetIV (m_IV); // save IV for SessionCreated + // decryption key for next block + uint8_t key[32]; + KeyDerivationFunction1 (m_Y, i2p::context.GetNTCP2StaticPrivateKey (), m_Y, key); + // verify MAC and decrypt options block (32 bytes), use m_H as AD + uint8_t nonce[12], options[16]; + memset (nonce, 0, 12); // set nonce to zero + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, m_H, 32, key, nonce, options, 16, false)) // decrypt + { + uint16_t version = bufbe16toh (options); + if (version == 2) + { + uint16_t paddingLen = bufbe16toh (options + 2); + m_SessionRequestBufferLen = paddingLen + 64; + // TODO: check tsA + if (paddingLen > 0) + boost::asio::async_read (m_Socket, boost::asio::buffer(m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + else + SendSessionCreated (); + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionRequest version mismatch ", version); + Terminate (); + } + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionRequest AEAD verification failed "); + Terminate (); + } + } + } + + void NTCP2Session::HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: SessionRequest padding read error: ", ecode.message ()); + Terminate (); + } + else + SendSessionCreated (); + } + + void NTCP2Session::SendSessionCreated () + { + m_SessionCreatedBuffer = new uint8_t[287]; // TODO: determine actual max size + // generate key pair (y) + uint8_t y[32]; + CreateEphemeralKey (y); + // encrypt Y + i2p::crypto::CBCEncryption encryption; + encryption.SetKey (i2p::context.GetIdentHash ()); + encryption.SetIV (m_IV); + encryption.Encrypt (y, 32, m_SessionCreatedBuffer); + // encryption key for next block (m_K) + KeyDerivationFunction2 (m_EphemeralPrivateKey, m_Y, m_SessionRequestBuffer, m_SessionRequestBufferLen, m_K); + auto paddingLen = rand () % (287 - 56); + uint8_t options[8]; + memset (options, 0, 8); + htobe16buf (options, paddingLen); // padLen + htobe32buf (options + 4, i2p::util::GetSecondsSinceEpoch ()); // tsB + // sign and encrypt options, use m_H as AD + uint8_t nonce[12]; + memset (nonce, 0, 12); // set nonce to zero + i2p::crypto::AEADChaCha20Poly1305 (options, 8, m_H, 32, m_K, nonce, m_SessionCreatedBuffer + 32, 24, true); // encrypt + // fill padding + RAND_bytes (m_SessionCreatedBuffer + 56, paddingLen); + // send message + boost::asio::async_write (m_Socket, boost::asio::buffer (m_SessionCreatedBuffer, paddingLen + 56), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionCreatedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + void NTCP2Session::HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) @@ -201,7 +289,7 @@ namespace transport decryption.SetIV (m_IV); decryption.Decrypt (m_SessionCreatedBuffer, 32, m_Y); // decryption key for next block (m_K) - KeyDerivationFunction2 (m_Y, m_SessionRequestBuffer, m_SessionRequestBufferLen, m_K); + KeyDerivationFunction2 (m_EphemeralPrivateKey, m_Y, m_SessionRequestBuffer, m_SessionRequestBufferLen, m_K); // decrypt and verify MAC uint8_t payload[8]; uint8_t nonce[12]; @@ -240,6 +328,7 @@ namespace transport } } + void NTCP2Session::SendSessionConfirmed () { // update AD @@ -287,11 +376,25 @@ namespace transport Terminate (); // TODO } + void NTCP2Session::HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + LogPrint (eLogDebug, "NTCP2: SessionCreated sent"); + Terminate (); // TODO + } + void NTCP2Session::ClientLogin () { SendSessionRequest (); } + void NTCP2Session::ServerLogin () + { + m_SessionRequestBuffer = new uint8_t[287]; // 287 bytes max for now + boost::asio::async_read (m_Socket, boost::asio::buffer(m_SessionRequestBuffer, 64), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + NTCP2Server::NTCP2Server (): m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service) { diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index e9a170bd..1bfcd0c2 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -25,20 +25,25 @@ namespace transport boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; void ClientLogin (); // Alice + void ServerLogin (); // Bob void SendI2NPMessages (const std::vector >& msgs) {}; // TODO private: void MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived); - void KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * pub, uint8_t * derived); // for SessionRequest - void KeyDerivationFunction2 (const uint8_t * pub, const uint8_t * sessionRequest, size_t sessionRequestLen, uint8_t * derived); // for SessionCreate + void KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * priv, const uint8_t * pub, uint8_t * derived); // for SessionRequest + void KeyDerivationFunction2 (const uint8_t * priv, const uint8_t * pub, const uint8_t * sessionRequest, size_t sessionRequestLen, uint8_t * derived); // for SessionCreate void KeyDerivationFunction3 (const uint8_t * staticPrivKey, uint8_t * derived); // for SessionConfirmed part 2 void CreateEphemeralKey (uint8_t * pub); void SendSessionRequest (); + void SendSessionCreated (); void SendSessionConfirmed (); void HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); @@ -49,8 +54,8 @@ namespace transport boost::asio::ip::tcp::socket m_Socket; bool m_IsEstablished, m_IsTerminated; - uint8_t m_ExpandedPrivateKey[64]; // x25519 ephemeral key - uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /* derived after SessionCreated */, m_Y[32]; + uint8_t m_EphemeralPrivateKey[32]; // x25519 + uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /* derived after SessionCreated */, m_Y[32] /* or X for Bob */; uint8_t * m_SessionRequestBuffer, * m_SessionCreatedBuffer, * m_SessionConfirmedBuffer; size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen; };