diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index 198f798b..85623f97 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -10,20 +10,15 @@ #include #include "Base.h" #include "Log.h" -#include "Crypto.h" #include "I2PEndian.h" #include "Timestamp.h" #include "RouterContext.h" #include "NetDb.hpp" #include "Tunnel.h" -#include "Transports.h" -#include "Garlic.h" -#include "ECIESX25519AEADRatchetSession.h" +#include "TransitTunnel.h" #include "I2NPProtocol.h" #include "version.h" -using namespace i2p::transport; - namespace i2p { std::shared_ptr NewI2NPMessage () @@ -376,108 +371,8 @@ namespace i2p return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo } - static bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) - { - for (int i = 0; i < num; i++) - { - uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE; - if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) - { - LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours"); - if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) - { - LogPrint (eLogWarning, "I2NP: Failed to decrypt tunnel build record"); - return false; - } - if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours - !(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint - { - LogPrint (eLogWarning, "I2NP: Next ident is ours in tunnel build record"); - return false; - } - uint8_t retCode = 0; - // decide if we should accept tunnel - bool accept = i2p::context.AcceptsTunnels (); - if (accept) - { - auto congestionLevel = i2p::context.GetCongestionLevel (false); - if (congestionLevel >= CONGESTION_LEVEL_MEDIUM) - { - if (congestionLevel < CONGESTION_LEVEL_FULL) - { - // random reject depending on congestion level - int level = i2p::tunnel::tunnels.GetRng ()() % (CONGESTION_LEVEL_FULL - CONGESTION_LEVEL_MEDIUM) + CONGESTION_LEVEL_MEDIUM; - if (congestionLevel > level) - accept = false; - } - else - accept = false; - } - } - // replace record to reply - if (accept) - { - auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), - clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, - clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, - clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, - clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); - if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel)) - retCode = 30; - } - else - retCode = 30; // always reject with bandwidth reason (30) - - memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options - record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; - // encrypt reply - i2p::crypto::CBCEncryption encryption; - for (int j = 0; j < num; j++) - { - uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; - if (j == i) - { - uint8_t nonce[12]; - memset (nonce, 0, 12); - auto& noiseState = i2p::context.GetCurrentNoiseState (); - if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, - noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt - { - LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed"); - return false; - } - } - else - { - encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); - encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); - encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); - } - } - return true; - } - } - return false; - } - static void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) { - int num = buf[0]; - LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records"); - if (num > i2p::tunnel::MAX_NUM_RECORDS) - { - LogPrint (eLogError, "I2NP: Too many records in VaribleTunnelBuild message ", num); - return; - } - if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1) - { - LogPrint (eLogError, "I2NP: VaribleTunnelBuild message of ", num, " records is too short ", len); - return; - } - auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID); if (tunnel) { @@ -496,24 +391,7 @@ namespace i2p } } else - { - uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (HandleBuildRequestRecords (num, buf + 1, clearText)) - { - if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel - { - // so we send it to reply tunnel - transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - eI2NPVariableTunnelBuildReply, buf, len, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } - else - transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } - } + i2p::tunnel::HandleVariableTransitTunnelBuildMsg (buf, len); } static void HandleTunnelBuildMsg (uint8_t * buf, size_t len) @@ -559,18 +437,6 @@ namespace i2p static void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) { - int num = buf[0]; - LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records"); - if (num > i2p::tunnel::MAX_NUM_RECORDS) - { - LogPrint (eLogError, "I2NP: Too many records in ShortTunnelBuild message ", num); - return; - } - if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) - { - LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len); - return; - } auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID); if (tunnel) { @@ -589,140 +455,8 @@ namespace i2p } return; } - const uint8_t * record = buf + 1; - for (int i = 0; i < num; i++) - { - if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) - { - LogPrint (eLogDebug, "I2NP: Short request record ", i, " is ours"); - uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) - { - LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i); - return; - } - if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES - { - LogPrint (eLogWarning, "I2NP: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record"); - return; - } - auto& noiseState = i2p::context.GetCurrentNoiseState (); - uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305 - i2p::crypto::AESKey layerKey, ivKey; // AES - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK); - memcpy (replyKey, noiseState.m_CK + 32, 32); - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK); - memcpy (layerKey, noiseState.m_CK + 32, 32); - bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - if (isEndpoint) - { - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK); - memcpy (ivKey, noiseState.m_CK + 32, 32); - } - else - { - if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours - { - LogPrint (eLogWarning, "I2NP: Next ident is ours in short request record"); - return; - } - memcpy (ivKey, noiseState.m_CK , 32); - } - - // check if we accept this tunnel - std::shared_ptr transitTunnel; - uint8_t retCode = 0; - if (!i2p::context.AcceptsTunnels () || i2p::context.GetCongestionLevel (false) >= CONGESTION_LEVEL_FULL) - retCode = 30; - if (!retCode) - { - // create new transit tunnel - transitTunnel = i2p::tunnel::CreateTransitTunnel ( - bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), - clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, - bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - layerKey, ivKey, - clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, - clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); - if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel)) - retCode = 30; - } - - // encrypt reply - uint8_t nonce[12]; - memset (nonce, 0, 12); - uint8_t * reply = buf + 1; - for (int j = 0; j < num; j++) - { - nonce[4] = j; // nonce is record # - if (j == i) - { - memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options - reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode; - if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, - noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt - { - LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed"); - return; - } - } - else - i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply); - reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; - } - // send reply - auto onDrop = [transitTunnel]() - { - if (transitTunnel) - { - auto t = transitTunnel->GetCreationTime (); - if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT) - // make transit tunnel expired - transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT); - } - }; - if (isEndpoint) - { - auto replyMsg = NewI2NPShortMessage (); - replyMsg->Concat (buf, len); - replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); - if (transitTunnel) replyMsg->onDrop = onDrop; - if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (), - clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local? - { - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK); - uint64_t tag; - memcpy (&tag, noiseState.m_CK, 8); - // we send it to reply tunnel - transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag))); - } - else - { - // IBGW is local - uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET); - auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID); - if (tunnel) - { - tunnel->SendTunnelDataMsg (replyMsg); - tunnel->FlushTunnelDataMsgs (); - } - else - LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply"); - } - } - else - { - auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, - bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); - if (transitTunnel) msg->onDrop = onDrop; - transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg); - } - return; - } - record += SHORT_TUNNEL_BUILD_RECORD_SIZE; - } + else + i2p::tunnel::HandleShortTransitTunnelBuildMsg (buf, len); } std::shared_ptr CreateTunnelDataMsg (const uint8_t * buf) diff --git a/libi2pd/TransitTunnel.cpp b/libi2pd/TransitTunnel.cpp index 72d7b8c2..71837a9f 100644 --- a/libi2pd/TransitTunnel.cpp +++ b/libi2pd/TransitTunnel.cpp @@ -8,9 +8,12 @@ #include #include "I2PEndian.h" +#include "Crypto.h" #include "Log.h" #include "RouterContext.h" #include "I2NPProtocol.h" +#include "Garlic.h" +#include "ECIESX25519AEADRatchetSession.h" #include "Tunnel.h" #include "Transports.h" #include "TransitTunnel.h" @@ -118,5 +121,274 @@ namespace tunnel return std::make_shared (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); } } + + void HandleShortTransitTunnelBuildMsg (uint8_t * buf, size_t len) + { + int num = buf[0]; + LogPrint (eLogDebug, "TransitTunnel: ShortTunnelBuild ", num, " records"); + if (num > i2p::tunnel::MAX_NUM_RECORDS) + { + LogPrint (eLogError, "TransitTunnel: Too many records in ShortTunnelBuild message ", num); + return; + } + if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) + { + LogPrint (eLogError, "TransitTunnel: ShortTunnelBuild message of ", num, " records is too short ", len); + return; + } + const uint8_t * record = buf + 1; + for (int i = 0; i < num; i++) + { + if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) + { + LogPrint (eLogDebug, "TransitTunnel: Short request record ", i, " is ours"); + uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) + { + LogPrint (eLogWarning, "TransitTunnel: Can't decrypt short request record ", i); + return; + } + if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES + { + LogPrint (eLogWarning, "TransitTunnel: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record"); + return; + } + auto& noiseState = i2p::context.GetCurrentNoiseState (); + uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305 + i2p::crypto::AESKey layerKey, ivKey; // AES + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK); + memcpy (replyKey, noiseState.m_CK + 32, 32); + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK); + memcpy (layerKey, noiseState.m_CK + 32, 32); + bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; + if (isEndpoint) + { + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK); + memcpy (ivKey, noiseState.m_CK + 32, 32); + } + else + { + if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours + { + LogPrint (eLogWarning, "TransitTunnel: Next ident is ours in short request record"); + return; + } + memcpy (ivKey, noiseState.m_CK , 32); + } + + // check if we accept this tunnel + std::shared_ptr transitTunnel; + uint8_t retCode = 0; + if (!i2p::context.AcceptsTunnels () || i2p::context.GetCongestionLevel (false) >= CONGESTION_LEVEL_FULL) + retCode = 30; + if (!retCode) + { + // create new transit tunnel + transitTunnel = i2p::tunnel::CreateTransitTunnel ( + bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), + clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, + bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + layerKey, ivKey, + clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, + clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); + if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel)) + retCode = 30; + } + + // encrypt reply + uint8_t nonce[12]; + memset (nonce, 0, 12); + uint8_t * reply = buf + 1; + for (int j = 0; j < num; j++) + { + nonce[4] = j; // nonce is record # + if (j == i) + { + memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options + reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode; + if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, + noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt + { + LogPrint (eLogWarning, "TransitTunnel: Short reply AEAD encryption failed"); + return; + } + } + else + i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply); + reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; + } + // send reply + auto onDrop = [transitTunnel]() + { + if (transitTunnel) + { + auto t = transitTunnel->GetCreationTime (); + if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT) + // make transit tunnel expired + transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT); + } + }; + if (isEndpoint) + { + auto replyMsg = NewI2NPShortMessage (); + replyMsg->Concat (buf, len); + replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); + if (transitTunnel) replyMsg->onDrop = onDrop; + if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (), + clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local? + { + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK); + uint64_t tag; + memcpy (&tag, noiseState.m_CK, 8); + // we send it to reply tunnel + i2p::transport::transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag))); + } + else + { + // IBGW is local + uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET); + auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID); + if (tunnel) + { + tunnel->SendTunnelDataMsg (replyMsg); + tunnel->FlushTunnelDataMsgs (); + } + else + LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply"); + } + } + else + { + auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, + bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); + if (transitTunnel) msg->onDrop = onDrop; + i2p::transport::transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg); + } + return; + } + record += SHORT_TUNNEL_BUILD_RECORD_SIZE; + } + } + + static bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) + { + for (int i = 0; i < num; i++) + { + uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE; + if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) + { + LogPrint (eLogDebug, "TransitTunnel: Build request record ", i, " is ours"); + if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) + { + LogPrint (eLogWarning, "TransitTunnel: Failed to decrypt tunnel build record"); + return false; + } + if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours + !(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint + { + LogPrint (eLogWarning, "TransitTunnel: Next ident is ours in tunnel build record"); + return false; + } + uint8_t retCode = 0; + // decide if we should accept tunnel + bool accept = i2p::context.AcceptsTunnels (); + if (accept) + { + auto congestionLevel = i2p::context.GetCongestionLevel (false); + if (congestionLevel >= CONGESTION_LEVEL_MEDIUM) + { + if (congestionLevel < CONGESTION_LEVEL_FULL) + { + // random reject depending on congestion level + int level = i2p::tunnel::tunnels.GetRng ()() % (CONGESTION_LEVEL_FULL - CONGESTION_LEVEL_MEDIUM) + CONGESTION_LEVEL_MEDIUM; + if (congestionLevel > level) + accept = false; + } + else + accept = false; + } + } + // replace record to reply + if (accept) + { + auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( + bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), + clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, + clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); + if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel)) + retCode = 30; + } + else + retCode = 30; // always reject with bandwidth reason (30) + + memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options + record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; + // encrypt reply + i2p::crypto::CBCEncryption encryption; + for (int j = 0; j < num; j++) + { + uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; + if (j == i) + { + uint8_t nonce[12]; + memset (nonce, 0, 12); + auto& noiseState = i2p::context.GetCurrentNoiseState (); + if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, + noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt + { + LogPrint (eLogWarning, "TransitTunnel: Reply AEAD encryption failed"); + return false; + } + } + else + { + encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); + encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); + encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); + } + } + return true; + } + } + return false; + } + + void HandleVariableTransitTunnelBuildMsg (uint8_t * buf, size_t len) + { + int num = buf[0]; + LogPrint (eLogDebug, "TransitTunnel: VariableTunnelBuild ", num, " records"); + if (num > i2p::tunnel::MAX_NUM_RECORDS) + { + LogPrint (eLogError, "TransitTunnle: Too many records in VaribleTunnelBuild message ", num); + return; + } + if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1) + { + LogPrint (eLogError, "TransitTunnel: VaribleTunnelBuild message of ", num, " records is too short ", len); + return; + } + uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + if (HandleBuildRequestRecords (num, buf + 1, clearText)) + { + if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel + { + // so we send it to reply tunnel + i2p::transport::transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + eI2NPVariableTunnelBuildReply, buf, len, + bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + } + else + i2p::transport::transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, + bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + } + } } } diff --git a/libi2pd/TransitTunnel.h b/libi2pd/TransitTunnel.h index b3381fb7..48615d19 100644 --- a/libi2pd/TransitTunnel.h +++ b/libi2pd/TransitTunnel.h @@ -108,6 +108,9 @@ namespace tunnel const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey, bool isGateway, bool isEndpoint); + + void HandleShortTransitTunnelBuildMsg (uint8_t * buf, size_t len); + void HandleVariableTransitTunnelBuildMsg (uint8_t * buf, size_t len); } }