@ -10,20 +10,15 @@
# include <atomic>
# include <atomic>
# include "Base.h"
# include "Base.h"
# include "Log.h"
# include "Log.h"
# include "Crypto.h"
# include "I2PEndian.h"
# include "I2PEndian.h"
# include "Timestamp.h"
# include "Timestamp.h"
# include "RouterContext.h"
# include "RouterContext.h"
# include "NetDb.hpp"
# include "NetDb.hpp"
# include "Tunnel.h"
# include "Tunnel.h"
# include "Transports.h"
# include "TransitTunnel.h"
# include "Garlic.h"
# include "ECIESX25519AEADRatchetSession.h"
# include "I2NPProtocol.h"
# include "I2NPProtocol.h"
# include "version.h"
# include "version.h"
using namespace i2p : : transport ;
namespace i2p
namespace i2p
{
{
std : : shared_ptr < I2NPMessage > NewI2NPMessage ( )
std : : shared_ptr < I2NPMessage > NewI2NPMessage ( )
@ -376,108 +371,8 @@ namespace i2p
return ! msg - > GetPayload ( ) [ DATABASE_STORE_TYPE_OFFSET ] ; // 0- RouterInfo
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 )
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 ) ;
auto tunnel = i2p : : tunnel : : tunnels . GetPendingInboundTunnel ( replyMsgID ) ;
if ( tunnel )
if ( tunnel )
{
{
@ -496,24 +391,7 @@ namespace i2p
}
}
}
}
else
else
{
i2p : : tunnel : : HandleVariableTransitTunnelBuildMsg ( buf , len ) ;
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 ) ) ) ;
}
}
}
}
static void HandleTunnelBuildMsg ( uint8_t * buf , size_t 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 )
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 ) ;
auto tunnel = i2p : : tunnel : : tunnels . GetPendingInboundTunnel ( replyMsgID ) ;
if ( tunnel )
if ( tunnel )
{
{
@ -589,140 +455,8 @@ namespace i2p
}
}
return ;
return ;
}
}
const uint8_t * record = buf + 1 ;
else
for ( int i = 0 ; i < num ; i + + )
i2p : : tunnel : : HandleShortTransitTunnelBuildMsg ( buf , len ) ;
{
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 < i2p : : tunnel : : TransitTunnel > 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 ;
}
}
}
std : : shared_ptr < I2NPMessage > CreateTunnelDataMsg ( const uint8_t * buf )
std : : shared_ptr < I2NPMessage > CreateTunnelDataMsg ( const uint8_t * buf )