From 7efb47fed4f950d974070c7bdec04d1284b8c6b3 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 2 Dec 2018 14:24:39 -0500 Subject: [PATCH] send NTCP2 frame from I2NP messages --- libi2pd/Crypto.cpp | 8 +- libi2pd/Crypto.h | 2 +- libi2pd/NTCP2.cpp | 114 +++++++++++++++++++++++----- libi2pd/NTCP2.h | 4 + tests/test-aeadchacha20poly1305.cpp | 2 +- 5 files changed, 106 insertions(+), 24 deletions(-) diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 28df1399..5629e75e 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -1174,7 +1174,7 @@ namespace crypto return ret; } - void AEADChaCha20Poly1305Encrypt (std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac) + void AEADChaCha20Poly1305Encrypt (std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac) { if (bufs.empty ()) return; #if LEGACY_OPENSSL @@ -1190,8 +1190,8 @@ namespace crypto size_t size = 0; for (auto& it: bufs) { - chacha::Chacha20Encrypt (state, (uint8_t *)it.first, it.second); - polyHash.Update ((uint8_t *)it.first, it.second); // after encryption + chacha::Chacha20Encrypt (state, it.first, it.second); + polyHash.Update (it.first, it.second); // after encryption size += it.second; } // padding @@ -1217,7 +1217,7 @@ namespace crypto EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce); for (auto& it: bufs) - EVP_EncryptUpdate(ctx, (uint8_t *)it.first, &outlen, (uint8_t *)it.first, it.second); + EVP_EncryptUpdate(ctx, it.first, &outlen, it.first, it.second); EVP_EncryptFinal_ex(ctx, NULL, &outlen); EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac); EVP_CIPHER_CTX_free (ctx); diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index 79d16e50..9e110e66 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -283,7 +283,7 @@ namespace crypto // AEAD/ChaCha20/Poly1305 bool AEADChaCha20Poly1305 (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, bool encrypt); // msgLen is len without tag - void AEADChaCha20Poly1305Encrypt (std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad + void AEADChaCha20Poly1305Encrypt (std::vector >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad // init and terminate void InitCrypto (bool precomputation); diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 513aad08..a2929bea 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -899,14 +899,9 @@ namespace transport m_Handler.Flush (); } - void NTCP2Session::SendNextFrame (const uint8_t * payload, size_t len) + void NTCP2Session::SetNextSentFrameLength (size_t frameLen, uint8_t * lengthBuf) { - if (IsTerminated ()) return; - uint8_t nonce[12]; - CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; - m_NextSendBuffer = new uint8_t[len + 16 + 2]; - i2p::crypto::AEADChaCha20Poly1305 (payload, len, nullptr, 0, m_SendKey, nonce, m_NextSendBuffer + 2, len + 16, true); -#if OPENSSL_SIPHASH + #if OPENSSL_SIPHASH EVP_DigestSignInit (m_SendMDCtx, nullptr, nullptr, nullptr, nullptr); EVP_DigestSignUpdate (m_SendMDCtx, m_SendIV.buf, 8); size_t l = 8; @@ -915,9 +910,19 @@ namespace transport i2p::crypto::Siphash<8> (m_SendIV.buf, m_SendIV.buf, 8, m_SendSipKey); #endif // length must be in BigEndian - htobe16buf (m_NextSendBuffer, (len + 16) ^ le16toh (m_SendIV.key)); - LogPrint (eLogDebug, "NTCP2: sent length ", len + 16); - + htobe16buf (lengthBuf, frameLen ^ le16toh (m_SendIV.key)); + LogPrint (eLogDebug, "NTCP2: sent length ", frameLen); + } + + void NTCP2Session::SendNextFrame (const uint8_t * payload, size_t len) + { + if (IsTerminated ()) return; + uint8_t nonce[12]; + CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; + m_NextSendBuffer = new uint8_t[len + 16 + 2]; + i2p::crypto::AEADChaCha20Poly1305 (payload, len, nullptr, 0, m_SendKey, nonce, m_NextSendBuffer + 2, len + 16, true); + SetNextSentFrameLength (len + 16, m_NextSendBuffer); + // send message m_IsSending = true; boost::asio::async_write (m_Socket, boost::asio::buffer (m_NextSendBuffer, len + 16 + 2), boost::asio::transfer_all (), @@ -943,6 +948,72 @@ namespace transport } } + void NTCP2Session::SendI2NPMsgs (std::vector >& msgs) + { + if (msgs.empty () || IsTerminated ()) return; + + size_t totalLen = 0; + std::vector > encryptBufs; + std::vector bufs; + std::shared_ptr first, last; + for (auto& it: msgs) + { + auto buf = it->GetNTCP2Header (); + auto len = it->GetNTCP2Length (); + encryptBufs.push_back (std::make_pair (buf, len)); + if (it == *msgs.begin ()) // first message + { + // allocate two bytes for length + buf -= 2; len += 2; + first = it; + } + if (it == *msgs.rbegin () && it->len + 16 < it->maxLen) // last message + { + // if it's long enough we add padding and MAC to it + // allocate 16 bytes for MAC + len += 16; + last = it; + // create padding block + auto paddingLen = CreatePaddingBlock (totalLen + len, buf + len, it->maxLen - len); + len += paddingLen; + totalLen += paddingLen; + it->len += paddingLen; + } + + bufs.push_back (boost::asio::buffer (buf, len)); + totalLen += len; + } + + uint8_t nonce[12]; + CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; + if (last) + i2p::crypto::AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, last->GetNTCP2Header () + last->GetNTCP2Length ()); + else // last block was not enough for MAC + { + // allocate send buffer + m_NextSendBuffer = new uint8_t[287]; // can be any size > 16, we just allocate 287 frequently + // crate padding block + auto paddingLen = CreatePaddingBlock (totalLen, m_NextSendBuffer, 287 - 16); + // and padding block to encrypt and send + encryptBufs.push_back (std::make_pair (m_NextSendBuffer, paddingLen)); + i2p::crypto::AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, m_NextSendBuffer + paddingLen); + bufs.push_back (boost::asio::buffer (m_NextSendBuffer, paddingLen + 16)); + totalLen += paddingLen; + } + SetNextSentFrameLength (totalLen + 16, first->GetNTCP2Header () - 2); // right before first block + + // send buffers + m_IsSending = true; + boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleI2NPMsgsSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs)); + } + + void NTCP2Session::HandleI2NPMsgsSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs) + { + HandleNextFrameSent (ecode, bytes_transferred); + // msgs get destroyed here + } + void NTCP2Session::SendQueue () { if (!m_SendQueue.empty ()) @@ -974,20 +1045,27 @@ namespace transport break; } // add padding block - int paddingSize = (s*NTCP2_MAX_PADDING_RATIO)/100; - if (s + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - s -3; - if (paddingSize) paddingSize = rand () % paddingSize; - payload[s] = eNTCP2BlkPadding; // blk - htobe16buf (payload + s + 1, paddingSize); // size - s += 3; - memset (payload + s, 0, paddingSize); - s += paddingSize; + s += CreatePaddingBlock (s, payload + s, NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - s); // send SendNextFrame (payload, s); m_Server.DeleteNTCP2FrameBuffer (buf); } } + size_t NTCP2Session::CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len) + { + if (len < 3) return 0; + len -= 3; + size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100; + if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - msgLen -3; + if (paddingSize > len) paddingSize = len; + if (paddingSize) paddingSize = rand () % paddingSize; + buf[0] = eNTCP2BlkPadding; // blk + htobe16buf (buf + 1, paddingSize); // size + memset (buf + 3, 0, paddingSize); + return paddingSize + 3; + } + void NTCP2Session::SendRouterInfo () { if (!IsEstablished ()) return; diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index eb46b2f8..20b60c0d 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -175,8 +175,12 @@ namespace transport void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void ProcessNextFrame (const uint8_t * frame, size_t len); + void SetNextSentFrameLength (size_t frameLen, uint8_t * lengthBuf); void SendNextFrame (const uint8_t * payload, size_t len); void HandleNextFrameSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void SendI2NPMsgs (std::vector >& msgs); + void HandleI2NPMsgsSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs); + size_t CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len); void SendQueue (); void SendRouterInfo (); void SendTermination (NTCP2TerminationReason reason); diff --git a/tests/test-aeadchacha20poly1305.cpp b/tests/test-aeadchacha20poly1305.cpp index d10ab2fc..822744c1 100644 --- a/tests/test-aeadchacha20poly1305.cpp +++ b/tests/test-aeadchacha20poly1305.cpp @@ -53,7 +53,7 @@ int main () assert (memcmp (buf1, text, 114) == 0); // test encryption of multiple buffers memcpy (buf, text, 114); - std::vector > bufs{ std::make_pair (buf, 50), std::make_pair (buf + 50, 50), std::make_pair (buf + 100, 14) }; + std::vector > bufs{ std::make_pair (buf, 50), std::make_pair (buf + 50, 50), std::make_pair (buf + 100, 14) }; i2p::crypto::AEADChaCha20Poly1305Encrypt (bufs, key, nonce, buf + 114); i2p::crypto::AEADChaCha20Poly1305 (buf, 114, nullptr, 0, key, nonce, buf1, 114, false); assert (memcmp (buf1, text, 114) == 0);