diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index b54a83ef..325af961 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -244,6 +244,119 @@ namespace transport SHA256_Final (m_H, &ctx); //h = SHA256(h || ciphertext) } + bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen) + { + // decrypt X + i2p::crypto::CBCDecryption decryption; + decryption.SetKey (i2p::context.GetIdentHash ()); + decryption.SetIV (i2p::context.GetNTCP2IV ()); + decryption.Decrypt (m_SessionRequestBuffer, 32, GetRemotePub ()); + decryption.GetIV (m_IV); // save IV for SessionCreated + // decryption key for next block + KDF1Bob (); + // 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, m_K, nonce, options, 16, false)) // decrypt + { + if (options[1] == 2) + { + paddingLen = bufbe16toh (options + 2); + m_SessionRequestBufferLen = paddingLen + 64; + m3p2Len = bufbe16toh (options + 4); + // TODO: check tsA + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionRequest version mismatch ", (int)options[1]); + return false; + } + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionRequest AEAD verification failed "); + return false; + } + return true; + } + + bool NTCP2Establisher::ProcessSessionCreatedMessage (uint16_t& paddingLen) + { + m_SessionCreatedBufferLen = 64; + // decrypt Y + i2p::crypto::CBCDecryption decryption; + decryption.SetKey (m_RemoteIdentHash); + decryption.SetIV (m_IV); + decryption.Decrypt (m_SessionCreatedBuffer, 32, GetRemotePub ()); + // decryption key for next block (m_K) + KDF2Alice (); + // decrypt and verify MAC + uint8_t payload[16]; + uint8_t nonce[12]; + memset (nonce, 0, 12); // set nonce to zero + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, m_H, 32, m_K, nonce, payload, 16, false)) // decrypt + { + paddingLen = bufbe16toh(payload + 2); + // TODO: check tsB + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionCreated AEAD verification failed "); + return false; + } + return true; + } + + bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce) + { + // update AD + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, m_SessionCreatedBuffer + 32, 32); // encrypted payload + SHA256_Final (m_H, &ctx); + + int paddingLength = m_SessionCreatedBufferLen - 64; + if (paddingLength > 0) + { + SHA256_CTX ctx1; + SHA256_Init (&ctx1); + SHA256_Update (&ctx1, m_H, 32); + SHA256_Update (&ctx1, m_SessionCreatedBuffer + 64, paddingLength); + SHA256_Final (m_H, &ctx1); + } + if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, m_H, 32, m_K, nonce, m_RemoteStaticKey, 32, false)) // decrypt S + { + LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed "); + return false; + } + return true; + } + + bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf) + { + // update AD again + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, m_SessionConfirmedBuffer, 48); + SHA256_Final (m_H, &ctx); + + KDF3Bob (); + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, m_H, 32, m_K, nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt + { + // caclulate new h again for KDF data + memcpy (m_SessionConfirmedBuffer + 16, m_H, 32); // h || ciphertext + SHA256 (m_SessionConfirmedBuffer + 16, m3p2Len + 32, m_H); //h = SHA256(h || ciphertext); + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed "); + return false; + } + return true; + } + NTCP2Session::NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter): TransportSession (in_RemoteRouter, NTCP2_ESTABLISH_TIMEOUT), m_Server (server), m_Socket (m_Server.GetService ()), @@ -368,52 +481,28 @@ namespace transport } else { - // decrypt X - i2p::crypto::CBCDecryption decryption; - decryption.SetKey (i2p::context.GetIdentHash ()); - decryption.SetIV (i2p::context.GetNTCP2IV ()); - decryption.Decrypt (m_Establisher->m_SessionRequestBuffer, 32, m_Establisher->GetRemotePub ()); - decryption.GetIV (m_Establisher->m_IV); // save IV for SessionCreated - // decryption key for next block - m_Establisher->KDF1Bob (); - // 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_Establisher->m_SessionRequestBuffer + 32, 16, m_Establisher->GetH (), 32, m_Establisher->GetK (), nonce, options, 16, false)) // decrypt + LogPrint (eLogDebug, "NTCP2: SessionRequest received ", bytes_transferred); + uint16_t paddingLen = 0; + if (m_Establisher->ProcessSessionRequestMessage (paddingLen)) { - if (options[1] == 2) + if (paddingLen > 0) { - uint16_t paddingLen = bufbe16toh (options + 2); - m_Establisher->m_SessionRequestBufferLen = paddingLen + 64; - m_Establisher->m3p2Len = bufbe16toh (options + 4); - // TODO: check tsA - if (paddingLen > 0) + if (paddingLen <= 287 - 64) // session request is 287 bytes max { - if (paddingLen <= 287 - 64) // session request is 287 bytes max - { - boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - else - { - LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long"); - Terminate (); - } + boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } else - SendSessionCreated (); + { + LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long"); + Terminate (); + } } else - { - LogPrint (eLogWarning, "NTCP2: SessionRequest version mismatch ", (int)options[1]); - Terminate (); - } - } + SendSessionCreated (); + } else - { - LogPrint (eLogWarning, "NTCP2: SessionRequest AEAD verification failed "); Terminate (); - } } } @@ -446,23 +535,9 @@ namespace transport else { LogPrint (eLogDebug, "NTCP2: SessionCreated received ", bytes_transferred); - m_Establisher->m_SessionCreatedBufferLen = 64; - // decrypt Y - i2p::crypto::CBCDecryption decryption; - decryption.SetKey (GetRemoteIdentity ()->GetIdentHash ()); - decryption.SetIV (m_Establisher->m_IV); - decryption.Decrypt (m_Establisher->m_SessionCreatedBuffer, 32, m_Establisher->GetRemotePub ()); - // decryption key for next block (m_K) - m_Establisher->KDF2Alice (); - // decrypt and verify MAC - uint8_t payload[16]; - uint8_t nonce[12]; - memset (nonce, 0, 12); // set nonce to zero - if (i2p::crypto::AEADChaCha20Poly1305 (m_Establisher->m_SessionCreatedBuffer + 32, 16, m_Establisher->GetH (), 32, m_Establisher->GetK (), nonce, payload, 16, false)) // decrypt - { - uint16_t paddingLen = bufbe16toh(payload + 2); - LogPrint (eLogDebug, "NTCP2: padding length ", paddingLen); - // TODO: check tsB + uint16_t paddingLen = 0; + if (m_Establisher->ProcessSessionCreatedMessage (paddingLen)) + { if (paddingLen > 0) { if (paddingLen <= 287 - 64) // session created is 287 bytes max @@ -480,10 +555,7 @@ namespace transport SendSessionConfirmed (); } else - { - LogPrint (eLogWarning, "NTCP2: SessionCreated AEAD verification failed "); Terminate (); - } } } @@ -559,38 +631,16 @@ namespace transport else { LogPrint (eLogDebug, "NTCP2: SessionConfirmed received"); - // update AD - uint8_t h[80]; - memcpy (h, m_Establisher->GetH (), 32); - memcpy (h + 32, m_Establisher->m_SessionCreatedBuffer + 32, 32); // encrypted payload - SHA256 (h, 64, h); - int paddingLength = m_Establisher->m_SessionCreatedBufferLen - 64; - if (paddingLength > 0) - { - SHA256_CTX ctx; - SHA256_Init (&ctx); - SHA256_Update (&ctx, h, 32); - SHA256_Update (&ctx, m_Establisher->m_SessionCreatedBuffer + 64, paddingLength); - SHA256_Final (h, &ctx); - } // part 1 uint8_t nonce[12]; CreateNonce (1, nonce); - if (i2p::crypto::AEADChaCha20Poly1305 (m_Establisher->m_SessionConfirmedBuffer, 32, h, 32, m_Establisher->GetK (), nonce, m_Establisher->m_RemoteStaticKey, 32, false)) // decrypt S + if (m_Establisher->ProcessSessionConfirmedMessagePart1 (nonce)) { // part 2 - // update AD again - memcpy (h + 32, m_Establisher->m_SessionConfirmedBuffer, 48); - SHA256 (h, 80, m_Establisher->m_H); - std::vector buf(m_Establisher->m3p2Len - 16); // -MAC - m_Establisher->KDF3Bob (); memset (nonce, 0, 12); // set nonce to 0 again - if (i2p::crypto::AEADChaCha20Poly1305 (m_Establisher->m_SessionConfirmedBuffer + 48, m_Establisher->m3p2Len - 16, m_Establisher->GetH (), 32, m_Establisher->GetK (), nonce, buf.data (), m_Establisher->m3p2Len - 16, false)) // decrypt + if (m_Establisher->ProcessSessionConfirmedMessagePart2 (nonce, buf.data ())) { - // caclulate new h again for KDF data - memcpy (m_Establisher->m_SessionConfirmedBuffer + 16, m_Establisher->GetH (), 32); // h || ciphertext - SHA256 (m_Establisher->m_SessionConfirmedBuffer + 16, m_Establisher->m3p2Len + 32, m_Establisher->m_H); //h = SHA256(h || ciphertext); KeyDerivationFunctionDataPhase (); // Bob data phase keys m_SendKey = m_Kba; @@ -599,7 +649,7 @@ namespace transport m_ReceiveSipKey = m_Sipkeysab; memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8); memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8); - + // payload // process RI if (buf[0] != eNTCP2BlkRouterInfo) { @@ -644,19 +694,13 @@ namespace transport SetRemoteIdentity (existing ? existing->GetRouterIdentity () : ri.GetRouterIdentity ()); m_Server.AddNTCP2Session (shared_from_this ()); Established (); - ReceiveLength (); - } + ReceiveLength (); + } else - { - LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed "); Terminate (); - } } else - { - LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed "); Terminate (); - } } } @@ -691,12 +735,20 @@ namespace transport else { i2p::crypto::Siphash<8> (m_ReceiveIV.buf, m_ReceiveIV.buf, 8, m_ReceiveSipKey); - // m_NextRecivedLen comes from the network in BigEndian + // m_NextReceivedLen comes from the network in BigEndian m_NextReceivedLen = be16toh (m_NextReceivedLen) ^ le16toh (m_ReceiveIV.key); LogPrint (eLogDebug, "NTCP2: received length ", m_NextReceivedLen); - if (m_NextReceivedBuffer) delete[] m_NextReceivedBuffer; - m_NextReceivedBuffer = new uint8_t[m_NextReceivedLen]; - Receive (); + if (m_NextReceivedLen >= 16) + { + if (m_NextReceivedBuffer) delete[] m_NextReceivedBuffer; + m_NextReceivedBuffer = new uint8_t[m_NextReceivedLen]; + Receive (); + } + else + { + LogPrint (eLogError, "NTCP2: received length ", m_NextReceivedLen, " is too short"); + Terminate (); + } } } diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index 3e202dc5..1f5700be 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -91,6 +91,11 @@ namespace transport void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce); void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce); + bool ProcessSessionRequestMessage (uint16_t& paddingLen); + bool ProcessSessionCreatedMessage (uint16_t& paddingLen); + bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce); + bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf); + BN_CTX * m_Ctx; uint8_t m_EphemeralPrivateKey[32], m_EphemeralPublicKey[32], m_RemoteEphemeralPublicKey[32]; // x25519 uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /*k*/;