send NTCP2 frame from I2NP messages

This commit is contained in:
orignal 2018-12-02 14:24:39 -05:00
parent 7692332f0e
commit 7efb47fed4
5 changed files with 106 additions and 24 deletions

View File

@ -1174,7 +1174,7 @@ namespace crypto
return ret;
}
void AEADChaCha20Poly1305Encrypt (std::vector<std::pair<void*, std::size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac)
void AEADChaCha20Poly1305Encrypt (std::vector<std::pair<uint8_t *, size_t> >& 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);

View File

@ -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<std::pair<void*, std::size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad
void AEADChaCha20Poly1305Encrypt (std::vector<std::pair<uint8_t *, size_t> >& 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);

View File

@ -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<std::shared_ptr<I2NPMessage> >& msgs)
{
if (msgs.empty () || IsTerminated ()) return;
size_t totalLen = 0;
std::vector<std::pair<uint8_t *, size_t> > encryptBufs;
std::vector<boost::asio::const_buffer> bufs;
std::shared_ptr<I2NPMessage> 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<std::shared_ptr<I2NPMessage> > 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;

View File

@ -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<std::shared_ptr<I2NPMessage> >& msgs);
void HandleI2NPMsgsSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs);
size_t CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len);
void SendQueue ();
void SendRouterInfo ();
void SendTermination (NTCP2TerminationReason reason);

View File

@ -53,7 +53,7 @@ int main ()
assert (memcmp (buf1, text, 114) == 0);
// test encryption of multiple buffers
memcpy (buf, text, 114);
std::vector<std::pair<void*, std::size_t> > bufs{ std::make_pair (buf, 50), std::make_pair (buf + 50, 50), std::make_pair (buf + 100, 14) };
std::vector<std::pair<uint8_t*, std::size_t> > 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);