2018-06-05 19:53:13 +03:00
# include <openssl/rand.h>
2018-06-05 22:37:08 +03:00
# include <openssl/sha.h>
# include <openssl/hmac.h>
2018-06-06 22:38:18 +03:00
# include <stdlib.h>
2018-06-13 23:16:23 +03:00
# include <vector>
2018-06-06 22:38:18 +03:00
# include "Log.h"
# include "I2PEndian.h"
2018-06-05 19:53:13 +03:00
# include "Crypto.h"
# include "Ed25519.h"
2018-06-21 19:39:24 +03:00
# include "Siphash.h"
2018-06-15 21:56:03 +03:00
# include "RouterContext.h"
2018-07-13 22:59:28 +03:00
# include "Transports.h"
2018-07-18 22:57:18 +03:00
# include "NetDb.hpp"
2018-06-05 19:53:13 +03:00
# include "NTCP2.h"
namespace i2p
{
namespace transport
{
2018-07-04 21:15:40 +03:00
NTCP2Establisher : : NTCP2Establisher ( )
{
m_Ctx = BN_CTX_new ( ) ;
CreateEphemeralKey ( ) ;
}
NTCP2Establisher : : ~ NTCP2Establisher ( )
{
BN_CTX_free ( m_Ctx ) ;
}
2018-07-03 23:26:02 +03:00
void NTCP2Establisher : : MixKey ( const uint8_t * inputKeyMaterial , uint8_t * derived )
2018-06-14 22:29:36 +03:00
{
// temp_key = HMAC-SHA256(ck, input_key_material)
uint8_t tempKey [ 32 ] ; unsigned int len ;
HMAC ( EVP_sha256 ( ) , m_CK , 32 , inputKeyMaterial , 32 , tempKey , & len ) ;
// ck = HMAC-SHA256(temp_key, byte(0x01))
static uint8_t one [ 1 ] = { 1 } ;
HMAC ( EVP_sha256 ( ) , tempKey , 32 , one , 1 , m_CK , & len ) ;
// derived = HMAC-SHA256(temp_key, ck || byte(0x02))
m_CK [ 32 ] = 2 ;
HMAC ( EVP_sha256 ( ) , tempKey , 32 , m_CK , 33 , derived , & len ) ;
}
2018-07-31 22:41:13 +03:00
void NTCP2Establisher : : KeyDerivationFunction1 ( const uint8_t * pub , const uint8_t * priv , const uint8_t * rs , const uint8_t * epub )
2018-06-05 22:37:08 +03:00
{
2018-06-20 23:09:22 +03:00
static const uint8_t protocolNameHash [ ] =
{
0x72 , 0xe8 , 0x42 , 0xc5 , 0x45 , 0xe1 , 0x80 , 0x80 , 0xd3 , 0x9c , 0x44 , 0x93 , 0xbb , 0x91 , 0xd7 , 0xed ,
0xf2 , 0x28 , 0x98 , 0x17 , 0x71 , 0x21 , 0x8c , 0x1f , 0x62 , 0x4e , 0x20 , 0x6f , 0x28 , 0xd3 , 0x2f , 0x71
} ; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256")
2018-07-18 23:27:43 +03:00
static const uint8_t hh [ 32 ] =
2018-06-20 23:09:22 +03:00
{
0x49 , 0xff , 0x48 , 0x3f , 0xc4 , 0x04 , 0xb9 , 0xb2 , 0x6b , 0x11 , 0x94 , 0x36 , 0x72 , 0xff , 0x05 , 0xb5 ,
0x61 , 0x27 , 0x03 , 0x31 , 0xba , 0x89 , 0xb8 , 0xfc , 0x33 , 0x15 , 0x93 , 0x87 , 0x57 , 0xdd , 0x3d , 0x1e
} ; // SHA256 (protocolNameHash)
memcpy ( m_CK , protocolNameHash , 32 ) ;
2018-07-19 16:27:59 +03:00
// h = SHA256(hh || rs)
SHA256_CTX ctx ;
SHA256_Init ( & ctx ) ;
SHA256_Update ( & ctx , hh , 32 ) ;
SHA256_Update ( & ctx , rs , 32 ) ;
SHA256_Final ( m_H , & ctx ) ;
2018-07-31 22:41:13 +03:00
// h = SHA256(h || epub)
2018-07-19 16:27:59 +03:00
SHA256_Init ( & ctx ) ;
SHA256_Update ( & ctx , m_H , 32 ) ;
2018-07-31 22:41:13 +03:00
SHA256_Update ( & ctx , epub , 32 ) ;
2018-07-19 16:27:59 +03:00
SHA256_Final ( m_H , & ctx ) ;
2018-06-05 22:37:08 +03:00
// x25519 between rs and priv
uint8_t inputKeyMaterial [ 32 ] ;
2018-07-31 22:41:13 +03:00
i2p : : crypto : : GetEd25519 ( ) - > ScalarMul ( pub , priv , inputKeyMaterial , m_Ctx ) ; // rs*priv
2018-07-04 21:15:40 +03:00
MixKey ( inputKeyMaterial , m_K ) ;
}
void NTCP2Establisher : : KDF1Alice ( )
{
2018-07-31 22:41:13 +03:00
KeyDerivationFunction1 ( m_RemoteStaticKey , GetPriv ( ) , m_RemoteStaticKey , GetPub ( ) ) ;
2018-07-04 21:15:40 +03:00
}
void NTCP2Establisher : : KDF1Bob ( )
{
2018-07-31 22:41:13 +03:00
KeyDerivationFunction1 ( GetRemotePub ( ) , i2p : : context . GetNTCP2StaticPrivateKey ( ) , i2p : : context . GetNTCP2StaticPublicKey ( ) , GetRemotePub ( ) ) ;
2018-06-13 23:16:23 +03:00
}
2018-07-31 22:41:13 +03:00
void NTCP2Establisher : : KeyDerivationFunction2 ( const uint8_t * sessionRequest , size_t sessionRequestLen , const uint8_t * epub )
2018-07-19 16:27:59 +03:00
{
SHA256_CTX ctx ;
SHA256_Init ( & ctx ) ;
SHA256_Update ( & ctx , m_H , 32 ) ;
SHA256_Update ( & ctx , sessionRequest + 32 , 32 ) ; // encrypted payload
SHA256_Final ( m_H , & ctx ) ;
2018-06-13 23:16:23 +03:00
int paddingLength = sessionRequestLen - 64 ;
if ( paddingLength > 0 )
{
2018-07-04 21:15:40 +03:00
SHA256_Init ( & ctx ) ;
2018-07-19 16:27:59 +03:00
SHA256_Update ( & ctx , m_H , 32 ) ;
2018-07-04 21:15:40 +03:00
SHA256_Update ( & ctx , sessionRequest + 64 , paddingLength ) ;
2018-07-19 16:27:59 +03:00
SHA256_Final ( m_H , & ctx ) ;
2018-06-13 23:16:23 +03:00
}
2018-07-19 16:27:59 +03:00
SHA256_Init ( & ctx ) ;
SHA256_Update ( & ctx , m_H , 32 ) ;
2018-07-31 22:41:13 +03:00
SHA256_Update ( & ctx , epub , 32 ) ;
2018-07-19 16:27:59 +03:00
SHA256_Final ( m_H , & ctx ) ;
2018-06-14 17:45:25 +03:00
2018-06-13 23:16:23 +03:00
// x25519 between remote pub and priv
uint8_t inputKeyMaterial [ 32 ] ;
2018-07-04 21:15:40 +03:00
i2p : : crypto : : GetEd25519 ( ) - > ScalarMul ( GetRemotePub ( ) , GetPriv ( ) , inputKeyMaterial , m_Ctx ) ;
MixKey ( inputKeyMaterial , m_K ) ;
2018-06-14 22:29:36 +03:00
}
2018-07-31 22:41:13 +03:00
void NTCP2Establisher : : KDF2Alice ( const uint8_t * sessionRequest , size_t sessionRequestLen )
{
KeyDerivationFunction2 ( sessionRequest , sessionRequestLen , GetRemotePub ( ) ) ;
}
void NTCP2Establisher : : KDF2Bob ( const uint8_t * sessionRequest , size_t sessionRequestLen )
{
KeyDerivationFunction2 ( sessionRequest , sessionRequestLen , GetPub ( ) ) ;
}
2018-07-09 22:56:23 +03:00
void NTCP2Establisher : : KDF3Alice ( )
2018-07-04 21:15:40 +03:00
{
uint8_t inputKeyMaterial [ 32 ] ;
2018-07-09 22:56:23 +03:00
i2p : : crypto : : GetEd25519 ( ) - > ScalarMul ( GetRemotePub ( ) , i2p : : context . GetNTCP2StaticPrivateKey ( ) , inputKeyMaterial , m_Ctx ) ;
MixKey ( inputKeyMaterial , m_K ) ;
}
void NTCP2Establisher : : KDF3Bob ( )
{
uint8_t inputKeyMaterial [ 32 ] ;
i2p : : crypto : : GetEd25519 ( ) - > ScalarMul ( m_RemoteStaticKey , m_EphemeralPrivateKey , inputKeyMaterial , m_Ctx ) ;
2018-07-04 21:15:40 +03:00
MixKey ( inputKeyMaterial , m_K ) ;
}
void NTCP2Establisher : : CreateEphemeralKey ( )
2018-07-03 23:26:02 +03:00
{
RAND_bytes ( m_EphemeralPrivateKey , 32 ) ;
2018-07-04 21:15:40 +03:00
i2p : : crypto : : GetEd25519 ( ) - > ScalarMulB ( m_EphemeralPrivateKey , m_EphemeralPublicKey , m_Ctx ) ;
2018-07-03 23:26:02 +03:00
}
NTCP2Session : : NTCP2Session ( NTCP2Server & server , std : : shared_ptr < const i2p : : data : : RouterInfo > in_RemoteRouter ) :
TransportSession ( in_RemoteRouter , 30 ) ,
m_Server ( server ) , m_Socket ( m_Server . GetService ( ) ) ,
m_IsEstablished ( false ) , m_IsTerminated ( false ) ,
m_SessionRequestBuffer ( nullptr ) , m_SessionCreatedBuffer ( nullptr ) , m_SessionConfirmedBuffer ( nullptr ) ,
m_NextReceivedBuffer ( nullptr ) , m_NextSendBuffer ( nullptr ) ,
2018-07-18 18:16:40 +03:00
m_ReceiveSequenceNumber ( 0 ) , m_SendSequenceNumber ( 0 ) , m_IsSending ( false )
2018-07-03 23:26:02 +03:00
{
m_Establisher . reset ( new NTCP2Establisher ) ;
2018-07-31 22:41:13 +03:00
if ( in_RemoteRouter ) // Alice
2018-07-03 23:26:02 +03:00
{
2018-07-31 22:41:13 +03:00
auto addr = in_RemoteRouter - > GetNTCP2Address ( ) ;
if ( addr - > ntcp2 )
{
memcpy ( m_Establisher - > m_RemoteStaticKey , addr - > ntcp2 - > staticKey , 32 ) ;
memcpy ( m_Establisher - > m_IV , addr - > ntcp2 - > iv , 16 ) ;
}
else
LogPrint ( eLogWarning , " NTCP2: Missing NTCP2 parameters " ) ;
2018-07-03 23:26:02 +03:00
}
}
NTCP2Session : : ~ NTCP2Session ( )
{
delete [ ] m_SessionRequestBuffer ;
delete [ ] m_SessionCreatedBuffer ;
delete [ ] m_SessionConfirmedBuffer ;
delete [ ] m_NextReceivedBuffer ;
delete [ ] m_NextSendBuffer ;
}
void NTCP2Session : : Terminate ( )
{
if ( ! m_IsTerminated )
{
m_IsTerminated = true ;
m_IsEstablished = false ;
m_Socket . close ( ) ;
2018-07-18 19:58:29 +03:00
transports . PeerDisconnected ( shared_from_this ( ) ) ;
m_Server . RemoveNTCP2Session ( shared_from_this ( ) ) ;
m_SendQueue . clear ( ) ;
2018-07-03 23:26:02 +03:00
LogPrint ( eLogDebug , " NTCP2: session terminated " ) ;
}
}
void NTCP2Session : : Done ( )
{
m_Server . GetService ( ) . post ( std : : bind ( & NTCP2Session : : Terminate , shared_from_this ( ) ) ) ;
}
2018-07-13 22:59:28 +03:00
void NTCP2Session : : Established ( )
{
m_IsEstablished = true ;
m_Establisher . reset ( nullptr ) ;
2018-07-18 18:16:40 +03:00
transports . PeerConnected ( shared_from_this ( ) ) ;
2018-07-13 22:59:28 +03:00
}
2018-07-03 23:26:02 +03:00
void NTCP2Session : : CreateNonce ( uint64_t seqn , uint8_t * nonce )
{
memset ( nonce , 0 , 4 ) ;
htole64buf ( nonce + 4 , seqn ) ;
}
2018-06-21 19:39:24 +03:00
void NTCP2Session : : KeyDerivationFunctionDataPhase ( )
2018-06-20 23:09:22 +03:00
{
uint8_t tempKey [ 32 ] ; unsigned int len ;
2018-07-03 23:26:02 +03:00
HMAC ( EVP_sha256 ( ) , m_Establisher - > GetCK ( ) , 32 , nullptr , 0 , tempKey , & len ) ; // temp_key = HMAC-SHA256(ck, zerolen)
2018-06-20 23:09:22 +03:00
static uint8_t one [ 1 ] = { 1 } ;
2018-06-21 19:39:24 +03:00
HMAC ( EVP_sha256 ( ) , tempKey , 32 , one , 1 , m_Kab , & len ) ; // k_ab = HMAC-SHA256(temp_key, byte(0x01)).
m_Kab [ 32 ] = 2 ;
2018-06-21 23:24:19 +03:00
HMAC ( EVP_sha256 ( ) , tempKey , 32 , m_Kab , 33 , m_Kba , & len ) ; // k_ba = HMAC-SHA256(temp_key, k_ab || byte(0x02))
2018-06-21 19:39:24 +03:00
static uint8_t ask [ 4 ] = { ' a ' , ' s ' , ' k ' , 1 } , master [ 32 ] ;
HMAC ( EVP_sha256 ( ) , tempKey , 32 , ask , 4 , master , & len ) ; // ask_master = HMAC-SHA256(temp_key, "ask" || byte(0x01))
uint8_t h [ 39 ] ;
2018-07-03 23:26:02 +03:00
memcpy ( h , m_Establisher - > GetH ( ) , 32 ) ;
2018-06-21 19:39:24 +03:00
memcpy ( h + 32 , " siphash " , 7 ) ;
HMAC ( EVP_sha256 ( ) , master , 32 , h , 39 , tempKey , & len ) ; // temp_key = HMAC-SHA256(ask_master, h || "siphash")
HMAC ( EVP_sha256 ( ) , tempKey , 32 , one , 1 , master , & len ) ; // sip_master = HMAC-SHA256(temp_key, byte(0x01))
HMAC ( EVP_sha256 ( ) , master , 32 , nullptr , 0 , tempKey , & len ) ; // temp_key = HMAC-SHA256(sip_master, zerolen)
2018-06-21 23:24:19 +03:00
HMAC ( EVP_sha256 ( ) , tempKey , 32 , one , 1 , m_Sipkeysab , & len ) ; // sipkeys_ab = HMAC-SHA256(temp_key, byte(0x01)).
m_Sipkeysab [ 32 ] = 2 ;
HMAC ( EVP_sha256 ( ) , tempKey , 32 , m_Sipkeysab , 33 , m_Sipkeysba , & len ) ; // sipkeys_ba = HMAC-SHA256(temp_key, sipkeys_ab || byte(0x02))
2018-06-20 23:09:22 +03:00
}
2018-06-05 19:53:13 +03:00
2018-06-06 22:38:18 +03:00
void NTCP2Session : : SendSessionRequest ( )
2018-06-05 19:53:13 +03:00
{
2018-06-11 21:32:15 +03:00
// create buffer and fill padding
auto paddingLength = rand ( ) % ( 287 - 64 ) ; // message length doesn't exceed 287 bytes
2018-06-13 23:16:23 +03:00
m_SessionRequestBufferLen = paddingLength + 64 ;
m_SessionRequestBuffer = new uint8_t [ m_SessionRequestBufferLen ] ;
2018-06-11 21:32:15 +03:00
RAND_bytes ( m_SessionRequestBuffer + 64 , paddingLength ) ;
2018-06-05 19:53:13 +03:00
// encrypt X
i2p : : crypto : : CBCEncryption encryption ;
encryption . SetKey ( GetRemoteIdentity ( ) - > GetIdentHash ( ) ) ;
2018-07-03 23:26:02 +03:00
encryption . SetIV ( m_Establisher - > m_IV ) ;
2018-07-04 21:15:40 +03:00
encryption . Encrypt ( m_Establisher - > GetPub ( ) , 32 , m_SessionRequestBuffer ) ; // X
2018-07-03 23:26:02 +03:00
encryption . GetIV ( m_Establisher - > m_IV ) ; // save IV for SessionCreated
2018-06-05 23:15:33 +03:00
// encryption key for next block
2018-07-04 21:15:40 +03:00
m_Establisher - > KDF1Alice ( ) ;
2018-06-06 22:38:18 +03:00
// fill options
uint8_t options [ 32 ] ; // actual options size is 16 bytes
memset ( options , 0 , 16 ) ;
2018-06-20 23:09:22 +03:00
options [ 1 ] = 2 ; // ver
2018-06-06 22:38:18 +03:00
htobe16buf ( options + 2 , paddingLength ) ; // padLen
2018-07-09 22:56:23 +03:00
m_Establisher - > m3p2Len = i2p : : context . GetRouterInfo ( ) . GetBufferLen ( ) + 20 ; // (RI header + RI + MAC for now) TODO: implement options
htobe16buf ( options + 4 , m_Establisher - > m3p2Len ) ;
2018-06-06 22:38:18 +03:00
// 2 bytes reserved
htobe32buf ( options + 8 , i2p : : util : : GetSecondsSinceEpoch ( ) ) ; // tsA
// 4 bytes reserved
2018-06-13 21:56:51 +03:00
// sign and encrypt options, use m_H as AD
2018-06-11 21:05:30 +03:00
uint8_t nonce [ 12 ] ;
2018-06-11 21:32:15 +03:00
memset ( nonce , 0 , 12 ) ; // set nonce to zero
2018-07-04 21:15:40 +03:00
i2p : : crypto : : AEADChaCha20Poly1305 ( options , 16 , m_Establisher - > GetH ( ) , 32 , m_Establisher - > GetK ( ) , nonce , m_SessionRequestBuffer + 32 , 32 , true ) ; // encrypt
2018-06-06 22:38:18 +03:00
// send message
2018-06-13 23:16:23 +03:00
boost : : asio : : async_write ( m_Socket , boost : : asio : : buffer ( m_SessionRequestBuffer , m_SessionRequestBufferLen ) , boost : : asio : : transfer_all ( ) ,
2018-06-06 22:38:18 +03:00
std : : bind ( & NTCP2Session : : HandleSessionRequestSent , shared_from_this ( ) , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
2018-06-05 19:53:13 +03:00
}
2018-06-06 22:38:18 +03:00
void NTCP2Session : : HandleSessionRequestSent ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
( void ) bytes_transferred ;
if ( ecode )
{
2018-06-13 21:56:51 +03:00
LogPrint ( eLogWarning , " NTCP2: couldn't send SessionRequest message: " , ecode . message ( ) ) ;
2018-06-11 19:29:30 +03:00
Terminate ( ) ;
}
else
{
m_SessionCreatedBuffer = new uint8_t [ 287 ] ; // TODO: determine actual max size
2018-06-20 23:09:22 +03:00
// we receive first 64 bytes (32 Y, and 32 ChaCha/Poly frame) first
boost : : asio : : async_read ( m_Socket , boost : : asio : : buffer ( m_SessionCreatedBuffer , 64 ) , boost : : asio : : transfer_all ( ) ,
2018-06-11 19:29:30 +03:00
std : : bind ( & NTCP2Session : : HandleSessionCreatedReceived , shared_from_this ( ) , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
2018-06-06 22:38:18 +03:00
}
}
2018-06-19 22:43:47 +03:00
void NTCP2Session : : HandleSessionRequestReceived ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
( void ) bytes_transferred ;
if ( ecode )
{
LogPrint ( eLogWarning , " NTCP2: SessionRequest read error: " , ecode . message ( ) ) ;
Terminate ( ) ;
}
else
{
// decrypt X
i2p : : crypto : : CBCDecryption decryption ;
decryption . SetKey ( i2p : : context . GetIdentHash ( ) ) ;
decryption . SetIV ( i2p : : context . GetNTCP2IV ( ) ) ;
2018-07-04 21:15:40 +03:00
decryption . Decrypt ( m_SessionRequestBuffer , 32 , m_Establisher - > GetRemotePub ( ) ) ;
2018-07-03 23:26:02 +03:00
decryption . GetIV ( m_Establisher - > m_IV ) ; // save IV for SessionCreated
2018-06-19 22:43:47 +03:00
// decryption key for next block
2018-07-04 21:15:40 +03:00
m_Establisher - > KDF1Bob ( ) ;
2018-06-19 22:43:47 +03:00
// 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
2018-07-04 21:15:40 +03:00
if ( i2p : : crypto : : AEADChaCha20Poly1305 ( m_SessionRequestBuffer + 32 , 16 , m_Establisher - > GetH ( ) , 32 , m_Establisher - > GetK ( ) , nonce , options , 16 , false ) ) // decrypt
2018-06-19 22:43:47 +03:00
{
2018-06-20 23:09:22 +03:00
if ( options [ 1 ] = = 2 )
2018-06-19 22:43:47 +03:00
{
uint16_t paddingLen = bufbe16toh ( options + 2 ) ;
m_SessionRequestBufferLen = paddingLen + 64 ;
2018-07-09 22:56:23 +03:00
m_Establisher - > m3p2Len = bufbe16toh ( options + 4 ) ;
2018-06-19 22:43:47 +03:00
// TODO: check tsA
if ( paddingLen > 0 )
boost : : asio : : async_read ( m_Socket , boost : : asio : : buffer ( m_SessionRequestBuffer + 64 , paddingLen ) , boost : : asio : : transfer_all ( ) ,
std : : bind ( & NTCP2Session : : HandleSessionRequestPaddingReceived , shared_from_this ( ) , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
else
SendSessionCreated ( ) ;
}
else
{
2018-06-20 23:09:22 +03:00
LogPrint ( eLogWarning , " NTCP2: SessionRequest version mismatch " , ( int ) options [ 1 ] ) ;
2018-06-19 22:43:47 +03:00
Terminate ( ) ;
}
}
else
{
LogPrint ( eLogWarning , " NTCP2: SessionRequest AEAD verification failed " ) ;
Terminate ( ) ;
}
}
}
void NTCP2Session : : HandleSessionRequestPaddingReceived ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
if ( ecode )
{
LogPrint ( eLogWarning , " NTCP2: SessionRequest padding read error: " , ecode . message ( ) ) ;
Terminate ( ) ;
}
else
SendSessionCreated ( ) ;
}
void NTCP2Session : : SendSessionCreated ( )
{
m_SessionCreatedBuffer = new uint8_t [ 287 ] ; // TODO: determine actual max size
// encrypt Y
i2p : : crypto : : CBCEncryption encryption ;
encryption . SetKey ( i2p : : context . GetIdentHash ( ) ) ;
2018-07-03 23:26:02 +03:00
encryption . SetIV ( m_Establisher - > m_IV ) ;
2018-07-04 21:15:40 +03:00
encryption . Encrypt ( m_Establisher - > GetPub ( ) , 32 , m_SessionCreatedBuffer ) ; // Y
2018-06-19 22:43:47 +03:00
// encryption key for next block (m_K)
2018-07-31 22:41:13 +03:00
m_Establisher - > KDF2Bob ( m_SessionRequestBuffer , m_SessionRequestBufferLen ) ;
2018-06-20 23:09:22 +03:00
auto paddingLen = rand ( ) % ( 287 - 64 ) ;
uint8_t options [ 16 ] ;
memset ( options , 0 , 16 ) ;
htobe16buf ( options + 2 , paddingLen ) ; // padLen
htobe32buf ( options + 8 , i2p : : util : : GetSecondsSinceEpoch ( ) ) ; // tsB
2018-06-19 22:43:47 +03:00
// sign and encrypt options, use m_H as AD
uint8_t nonce [ 12 ] ;
memset ( nonce , 0 , 12 ) ; // set nonce to zero
2018-07-04 21:15:40 +03:00
i2p : : crypto : : AEADChaCha20Poly1305 ( options , 16 , m_Establisher - > GetH ( ) , 32 , m_Establisher - > GetK ( ) , nonce , m_SessionCreatedBuffer + 32 , 32 , true ) ; // encrypt
2018-06-19 22:43:47 +03:00
// fill padding
2018-07-31 22:41:13 +03:00
RAND_bytes ( m_SessionCreatedBuffer + 64 , paddingLen ) ;
2018-06-19 22:43:47 +03:00
// send message
2018-07-09 22:56:23 +03:00
m_SessionCreatedBufferLen = paddingLen + 64 ;
boost : : asio : : async_write ( m_Socket , boost : : asio : : buffer ( m_SessionCreatedBuffer , m_SessionCreatedBufferLen ) , boost : : asio : : transfer_all ( ) ,
2018-06-19 22:43:47 +03:00
std : : bind ( & NTCP2Session : : HandleSessionCreatedSent , shared_from_this ( ) , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
}
2018-06-11 19:29:30 +03:00
void NTCP2Session : : HandleSessionCreatedReceived ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
if ( ecode )
2018-06-13 21:56:51 +03:00
{
LogPrint ( eLogWarning , " NTCP2: SessionCreated read error: " , ecode . message ( ) ) ;
Terminate ( ) ;
}
else
{
2018-06-14 22:29:36 +03:00
LogPrint ( eLogDebug , " NTCP2: SessionCreated received " , bytes_transferred ) ;
2018-06-20 23:09:22 +03:00
m_SessionCreatedBufferLen = 64 ;
2018-06-13 21:56:51 +03:00
// decrypt Y
i2p : : crypto : : CBCDecryption decryption ;
decryption . SetKey ( GetRemoteIdentity ( ) - > GetIdentHash ( ) ) ;
2018-07-03 23:26:02 +03:00
decryption . SetIV ( m_Establisher - > m_IV ) ;
2018-07-04 21:15:40 +03:00
decryption . Decrypt ( m_SessionCreatedBuffer , 32 , m_Establisher - > GetRemotePub ( ) ) ;
2018-06-14 22:29:36 +03:00
// decryption key for next block (m_K)
2018-07-31 22:41:13 +03:00
m_Establisher - > KDF2Alice ( m_SessionRequestBuffer , m_SessionRequestBufferLen ) ;
2018-06-13 23:16:23 +03:00
// decrypt and verify MAC
2018-06-20 23:09:22 +03:00
uint8_t payload [ 16 ] ;
2018-06-13 23:16:23 +03:00
uint8_t nonce [ 12 ] ;
memset ( nonce , 0 , 12 ) ; // set nonce to zero
2018-07-04 21:15:40 +03:00
if ( i2p : : crypto : : AEADChaCha20Poly1305 ( m_SessionCreatedBuffer + 32 , 16 , m_Establisher - > GetH ( ) , 32 , m_Establisher - > GetK ( ) , nonce , payload , 16 , false ) ) // decrypt
2018-06-13 23:16:23 +03:00
{
2018-06-20 23:09:22 +03:00
uint16_t paddingLen = bufbe16toh ( payload + 2 ) ;
2018-06-14 22:29:36 +03:00
LogPrint ( eLogDebug , " NTCP2: padding length " , paddingLen ) ;
2018-06-20 23:09:22 +03:00
// TODO: check tsB
2018-06-14 22:29:36 +03:00
if ( paddingLen > 0 )
{
2018-06-20 23:09:22 +03:00
boost : : asio : : async_read ( m_Socket , boost : : asio : : buffer ( m_SessionCreatedBuffer + 64 , paddingLen ) , boost : : asio : : transfer_all ( ) ,
2018-06-14 22:29:36 +03:00
std : : bind ( & NTCP2Session : : HandleSessionCreatedPaddingReceived , shared_from_this ( ) , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
}
else
SendSessionConfirmed ( ) ;
2018-06-13 23:16:23 +03:00
}
else
{
2018-07-31 22:41:13 +03:00
LogPrint ( eLogWarning , " NTCP2: SessionCreated AEAD verification failed " ) ;
2018-06-13 23:16:23 +03:00
Terminate ( ) ;
}
2018-06-13 21:56:51 +03:00
}
2018-06-11 19:29:30 +03:00
}
2018-06-14 22:29:36 +03:00
void NTCP2Session : : HandleSessionCreatedPaddingReceived ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
if ( ecode )
{
LogPrint ( eLogWarning , " NTCP2: SessionCreated padding read error: " , ecode . message ( ) ) ;
Terminate ( ) ;
}
else
{
m_SessionCreatedBufferLen + = bytes_transferred ;
SendSessionConfirmed ( ) ;
}
}
2018-06-19 22:43:47 +03:00
2018-06-14 22:29:36 +03:00
void NTCP2Session : : SendSessionConfirmed ( )
{
// update AD
uint8_t h [ 80 ] ;
2018-07-03 23:26:02 +03:00
memcpy ( h , m_Establisher - > GetH ( ) , 32 ) ;
2018-06-20 23:09:22 +03:00
memcpy ( h + 32 , m_SessionCreatedBuffer + 32 , 32 ) ; // encrypted payload
SHA256 ( h , 64 , h ) ;
int paddingLength = m_SessionCreatedBufferLen - 64 ;
2018-06-14 22:29:36 +03:00
if ( paddingLength > 0 )
{
2018-07-04 21:15:40 +03:00
SHA256_CTX ctx ;
SHA256_Init ( & ctx ) ;
SHA256_Update ( & ctx , h , 32 ) ;
SHA256_Update ( & ctx , m_SessionCreatedBuffer + 64 , paddingLength ) ;
SHA256_Final ( h , & ctx ) ;
2018-06-14 22:29:36 +03:00
}
// part1 48 bytes
m_SessionConfirmedBuffer = new uint8_t [ 2048 ] ; // TODO: actual size
uint8_t nonce [ 12 ] ;
2018-06-25 19:28:07 +03:00
CreateNonce ( 1 , nonce ) ;
2018-07-04 21:15:40 +03:00
i2p : : crypto : : AEADChaCha20Poly1305 ( i2p : : context . GetNTCP2StaticPublicKey ( ) , 32 , h , 32 , m_Establisher - > GetK ( ) , nonce , m_SessionConfirmedBuffer , 48 , true ) ; // encrypt
2018-06-14 22:29:36 +03:00
// part 2
// update AD again
memcpy ( h + 32 , m_SessionConfirmedBuffer , 48 ) ;
2018-07-03 23:26:02 +03:00
SHA256 ( h , 80 , m_Establisher - > m_H ) ;
2018-06-14 22:29:36 +03:00
2018-07-09 22:56:23 +03:00
std : : vector < uint8_t > buf ( m_Establisher - > m3p2Len - 16 ) ; // -MAC
2018-06-15 21:56:03 +03:00
buf [ 0 ] = 2 ; // block
htobe16buf ( buf . data ( ) + 1 , i2p : : context . GetRouterInfo ( ) . GetBufferLen ( ) + 1 ) ; // flag + RI
buf [ 3 ] = 0 ; // flag
memcpy ( buf . data ( ) + 4 , i2p : : context . GetRouterInfo ( ) . GetBuffer ( ) , i2p : : context . GetRouterInfo ( ) . GetBufferLen ( ) ) ;
2018-07-09 22:56:23 +03:00
m_Establisher - > KDF3Alice ( ) ;
2018-06-14 22:29:36 +03:00
memset ( nonce , 0 , 12 ) ; // set nonce to 0 again
2018-07-09 22:56:23 +03:00
i2p : : crypto : : AEADChaCha20Poly1305 ( buf . data ( ) , m_Establisher - > m3p2Len - 16 , m_Establisher - > GetH ( ) , 32 , m_Establisher - > GetK ( ) , nonce , m_SessionConfirmedBuffer + 48 , m_Establisher - > m3p2Len , true ) ; // encrypt
2018-06-21 23:24:19 +03:00
uint8_t tmp [ 48 ] ;
memcpy ( tmp , m_SessionConfirmedBuffer , 48 ) ;
2018-07-03 23:26:02 +03:00
memcpy ( m_SessionConfirmedBuffer + 16 , m_Establisher - > GetH ( ) , 32 ) ; // h || ciphertext
2018-07-09 22:56:23 +03:00
SHA256 ( m_SessionConfirmedBuffer + 16 , m_Establisher - > m3p2Len + 32 , m_Establisher - > m_H ) ; //h = SHA256(h || ciphertext);
2018-06-21 23:24:19 +03:00
memcpy ( m_SessionConfirmedBuffer , tmp , 48 ) ;
2018-06-14 22:29:36 +03:00
// send message
2018-07-09 22:56:23 +03:00
boost : : asio : : async_write ( m_Socket , boost : : asio : : buffer ( m_SessionConfirmedBuffer , m_Establisher - > m3p2Len + 48 ) , boost : : asio : : transfer_all ( ) ,
2018-06-14 22:29:36 +03:00
std : : bind ( & NTCP2Session : : HandleSessionConfirmedSent , shared_from_this ( ) , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
}
void NTCP2Session : : HandleSessionConfirmedSent ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
LogPrint ( eLogDebug , " NTCP2: SessionConfirmed sent " ) ;
2018-06-21 19:39:24 +03:00
KeyDerivationFunctionDataPhase ( ) ;
2018-07-09 22:56:23 +03:00
// Alice
m_SendKey = m_Kab ;
m_ReceiveKey = m_Kba ;
m_SendSipKey = m_Sipkeysab ;
m_ReceiveSipKey = m_Sipkeysba ;
memcpy ( m_ReceiveIV , m_Sipkeysba + 16 , 8 ) ;
memcpy ( m_SendIV , m_Sipkeysab + 16 , 8 ) ;
2018-07-13 22:59:28 +03:00
Established ( ) ;
2018-06-21 23:24:19 +03:00
ReceiveLength ( ) ;
2018-06-25 19:28:07 +03:00
// TODO: remove
2018-07-18 18:16:40 +03:00
//m_SendQueue.push_back (CreateDeliveryStatusMsg (1));
//SendQueue ();
2018-06-14 22:29:36 +03:00
}
2018-06-19 22:43:47 +03:00
void NTCP2Session : : HandleSessionCreatedSent ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
2018-07-09 22:56:23 +03:00
( void ) bytes_transferred ;
if ( ecode )
{
LogPrint ( eLogWarning , " NTCP2: couldn't send SessionCreated message: " , ecode . message ( ) ) ;
Terminate ( ) ;
}
else
{
LogPrint ( eLogDebug , " NTCP2: SessionCreated sent " ) ;
m_SessionConfirmedBuffer = new uint8_t [ m_Establisher - > m3p2Len + 48 ] ;
boost : : asio : : async_read ( m_Socket , boost : : asio : : buffer ( m_SessionConfirmedBuffer , m_Establisher - > m3p2Len + 48 ) , boost : : asio : : transfer_all ( ) ,
std : : bind ( & NTCP2Session : : HandleSessionConfirmedReceived , shared_from_this ( ) , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
}
}
void NTCP2Session : : HandleSessionConfirmedReceived ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
if ( ecode )
{
2018-07-31 22:41:13 +03:00
LogPrint ( eLogWarning , " NTCP2: SessionConfirmed Part1 read error: " , ecode . message ( ) ) ;
2018-07-09 22:56:23 +03:00
Terminate ( ) ;
}
else
{
2018-07-31 22:41:13 +03:00
LogPrint ( eLogDebug , " NTCP2: SessionConfirmed Part1 received " ) ;
2018-07-09 22:56:23 +03:00
// update AD
uint8_t h [ 80 ] ;
memcpy ( h , m_Establisher - > GetH ( ) , 32 ) ;
memcpy ( h + 32 , m_SessionCreatedBuffer + 32 , 32 ) ; // encrypted payload
SHA256 ( h , 64 , h ) ;
int paddingLength = m_SessionCreatedBufferLen - 64 ;
if ( paddingLength > 0 )
{
SHA256_CTX ctx ;
SHA256_Init ( & ctx ) ;
SHA256_Update ( & ctx , h , 32 ) ;
SHA256_Update ( & ctx , m_SessionCreatedBuffer + 64 , paddingLength ) ;
SHA256_Final ( h , & ctx ) ;
}
// part 1
uint8_t nonce [ 12 ] ;
CreateNonce ( 1 , nonce ) ;
2018-07-31 22:41:13 +03:00
if ( i2p : : crypto : : AEADChaCha20Poly1305 ( m_SessionConfirmedBuffer , 32 , h , 32 , m_Establisher - > GetK ( ) , nonce , m_Establisher - > m_RemoteStaticKey , 32 , false ) ) // decrypt S
2018-07-18 22:57:18 +03:00
{
2018-07-31 22:41:13 +03:00
// part 2
// update AD again
memcpy ( h + 32 , m_SessionConfirmedBuffer , 48 ) ;
SHA256 ( h , 80 , m_Establisher - > m_H ) ;
std : : vector < uint8_t > buf ( m_Establisher - > m3p2Len - 16 ) ; // -MAC
m_Establisher - > KDF3Bob ( ) ;
memset ( nonce , 0 , 12 ) ; // set nonce to 0 again
if ( i2p : : crypto : : AEADChaCha20Poly1305 ( m_SessionConfirmedBuffer + 48 , m_Establisher - > m3p2Len - 16 , m_Establisher - > GetH ( ) , 32 , m_Establisher - > GetK ( ) , nonce , buf . data ( ) , m_Establisher - > m3p2Len - 16 , false ) ) // decrypt
2018-07-18 22:57:18 +03:00
{
2018-07-31 22:41:13 +03:00
// process RI
if ( buf [ 0 ] = = eNTCP2BlkRouterInfo )
{
auto size = bufbe16toh ( buf . data ( ) + 1 ) ;
if ( size < = buf . size ( ) - 3 )
{
// TODO: check flag
i2p : : data : : netdb . AddRouterInfo ( buf . data ( ) + 4 , size - 1 ) ; // 1 byte block type + 2 bytes size + 1 byte flag
// TODO: process options
}
else
LogPrint ( eLogError , " NTCP2: Unexpected RouterInfo size " , size , " in SessionConfirmed " ) ;
}
else
LogPrint ( eLogWarning , " NTCP2: unexpected block " , ( int ) buf [ 0 ] , " in SessionConfirmed " ) ;
// caclulate new h again for KDF data
memcpy ( m_SessionConfirmedBuffer + 16 , m_Establisher - > GetH ( ) , 32 ) ; // h || ciphertext
SHA256 ( m_SessionConfirmedBuffer + 16 , m_Establisher - > m3p2Len + 32 , m_Establisher - > m_H ) ; //h = SHA256(h || ciphertext);
KeyDerivationFunctionDataPhase ( ) ;
// Bob
m_SendKey = m_Kba ;
m_ReceiveKey = m_Kab ;
m_SendSipKey = m_Sipkeysba ;
m_ReceiveSipKey = m_Sipkeysab ;
memcpy ( m_ReceiveIV , m_Sipkeysab + 16 , 8 ) ;
memcpy ( m_SendIV , m_Sipkeysba + 16 , 8 ) ;
Established ( ) ;
ReceiveLength ( ) ;
}
2018-07-18 22:57:18 +03:00
else
2018-07-31 22:41:13 +03:00
{
LogPrint ( eLogWarning , " NTCP2: SessionConfirmed Part2 AEAD verification failed " ) ;
Terminate ( ) ;
}
2018-07-18 22:57:18 +03:00
}
else
2018-07-31 22:41:13 +03:00
{
LogPrint ( eLogWarning , " NTCP2: SessionConfirmed Part1 AEAD verification failed " ) ;
Terminate ( ) ;
}
2018-07-09 22:56:23 +03:00
}
2018-06-19 22:43:47 +03:00
}
2018-06-06 22:38:18 +03:00
void NTCP2Session : : ClientLogin ( )
{
SendSessionRequest ( ) ;
}
2018-06-11 19:29:30 +03:00
2018-06-19 22:43:47 +03:00
void NTCP2Session : : ServerLogin ( )
{
m_SessionRequestBuffer = new uint8_t [ 287 ] ; // 287 bytes max for now
boost : : asio : : async_read ( m_Socket , boost : : asio : : buffer ( m_SessionRequestBuffer , 64 ) , boost : : asio : : transfer_all ( ) ,
std : : bind ( & NTCP2Session : : HandleSessionRequestReceived , shared_from_this ( ) ,
std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
}
2018-06-21 23:24:19 +03:00
void NTCP2Session : : ReceiveLength ( )
{
boost : : asio : : async_read ( m_Socket , boost : : asio : : buffer ( & m_NextReceivedLen , 2 ) , boost : : asio : : transfer_all ( ) ,
std : : bind ( & NTCP2Session : : HandleReceivedLength , shared_from_this ( ) , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
}
void NTCP2Session : : HandleReceivedLength ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
if ( ecode )
{
LogPrint ( eLogWarning , " NTCP2: receive length read error: " , ecode . message ( ) ) ;
Terminate ( ) ;
}
else
{
2018-07-09 22:56:23 +03:00
i2p : : crypto : : Siphash < 8 > ( m_ReceiveIV , m_ReceiveIV , 8 , m_ReceiveSipKey ) ;
2018-06-22 19:20:35 +03:00
m_NextReceivedLen = be16toh ( m_NextReceivedLen ^ bufbe16toh ( m_ReceiveIV ) ) ;
2018-06-21 23:24:19 +03:00
LogPrint ( eLogDebug , " NTCP2: received length " , m_NextReceivedLen ) ;
delete [ ] m_NextReceivedBuffer ;
m_NextReceivedBuffer = new uint8_t [ m_NextReceivedLen ] ;
Receive ( ) ;
}
}
void NTCP2Session : : Receive ( )
{
boost : : asio : : async_read ( m_Socket , boost : : asio : : buffer ( m_NextReceivedBuffer , m_NextReceivedLen ) , boost : : asio : : transfer_all ( ) ,
std : : bind ( & NTCP2Session : : HandleReceived , shared_from_this ( ) , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
}
void NTCP2Session : : HandleReceived ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
if ( ecode )
{
LogPrint ( eLogWarning , " NTCP2: receive read error: " , ecode . message ( ) ) ;
Terminate ( ) ;
}
2018-06-22 22:02:49 +03:00
else
{
2018-07-18 22:57:18 +03:00
m_NumReceivedBytes + = bytes_transferred + 2 ; // + length
i2p : : transport : : transports . UpdateReceivedBytes ( bytes_transferred ) ;
2018-06-22 22:02:49 +03:00
uint8_t nonce [ 12 ] ;
2018-06-25 19:28:07 +03:00
CreateNonce ( m_ReceiveSequenceNumber , nonce ) ; m_ReceiveSequenceNumber + + ;
2018-06-22 22:02:49 +03:00
uint8_t * decrypted = new uint8_t [ m_NextReceivedLen ] ;
2018-07-09 22:56:23 +03:00
if ( i2p : : crypto : : AEADChaCha20Poly1305 ( m_NextReceivedBuffer , m_NextReceivedLen - 16 , nullptr , 0 , m_ReceiveKey , nonce , decrypted , m_NextReceivedLen , false ) )
2018-06-22 22:02:49 +03:00
{
2018-07-18 21:19:12 +03:00
LogPrint ( eLogDebug , " NTCP2: received message decrypted " ) ;
2018-06-22 22:02:49 +03:00
ProcessNextFrame ( decrypted , m_NextReceivedLen - 16 ) ;
ReceiveLength ( ) ;
}
else
{
LogPrint ( eLogWarning , " NTCP2: Received MAC verification failed " ) ;
Terminate ( ) ;
}
delete [ ] decrypted ;
}
}
void NTCP2Session : : ProcessNextFrame ( const uint8_t * frame , size_t len )
{
size_t offset = 0 ;
while ( offset < len )
{
uint8_t blk = frame [ offset ] ;
offset + + ;
auto size = bufbe16toh ( frame + offset ) ;
offset + = 2 ;
LogPrint ( eLogDebug , " NTCP2: Block type " , ( int ) blk , " of size " , size ) ;
if ( size > len )
{
LogPrint ( eLogError , " NTCP2: Unexpected block length " , size ) ;
break ;
}
2018-07-17 22:17:05 +03:00
switch ( blk )
{
case eNTCP2BlkDateTime :
LogPrint ( eLogDebug , " NTCP2: datetime " ) ;
break ;
case eNTCP2BlkOptions :
LogPrint ( eLogDebug , " NTCP2: options " ) ;
break ;
case eNTCP2BlkRouterInfo :
2018-07-18 22:57:18 +03:00
{
2018-07-18 23:27:43 +03:00
LogPrint ( eLogDebug , " NTCP2: RouterInfo flag= " , ( int ) frame [ offset ] ) ;
2018-07-18 22:57:18 +03:00
i2p : : data : : netdb . AddRouterInfo ( frame + offset + 1 , size - 1 ) ;
break ;
}
2018-07-17 22:17:05 +03:00
case eNTCP2BlkI2NPMessage :
{
LogPrint ( eLogDebug , " NTCP2: I2NP " ) ;
auto nextMsg = NewI2NPMessage ( size ) ;
nextMsg - > len = nextMsg - > offset + size + 7 ; // 7 more bytes for full I2NP header
memcpy ( nextMsg - > GetNTCP2Header ( ) , frame + offset , size ) ;
nextMsg - > FromNTCP2 ( ) ;
m_Handler . PutNextMessage ( nextMsg ) ;
break ;
}
case eNTCP2BlkTermination :
LogPrint ( eLogDebug , " NTCP2: termination " ) ;
break ;
case eNTCP2BlkPadding :
LogPrint ( eLogDebug , " NTCP2: padding " ) ;
break ;
default :
LogPrint ( eLogWarning , " NTCP2: Unknown block type " , ( int ) blk ) ;
}
2018-06-22 22:02:49 +03:00
offset + = size ;
}
2018-07-17 22:17:05 +03:00
m_Handler . Flush ( ) ;
2018-06-21 23:24:19 +03:00
}
2018-06-25 19:28:07 +03:00
void NTCP2Session : : SendNextFrame ( const uint8_t * payload , size_t len )
{
uint8_t nonce [ 12 ] ;
CreateNonce ( m_SendSequenceNumber , nonce ) ; m_SendSequenceNumber + + ;
m_NextSendBuffer = new uint8_t [ len + 16 + 2 ] ;
2018-07-09 22:56:23 +03:00
i2p : : crypto : : AEADChaCha20Poly1305 ( payload , len , nullptr , 0 , m_SendKey , nonce , m_NextSendBuffer + 2 , len + 16 , true ) ;
i2p : : crypto : : Siphash < 8 > ( m_SendIV , m_SendIV , 8 , m_SendSipKey ) ;
2018-06-25 19:28:07 +03:00
htobuf16 ( m_NextSendBuffer , bufbe16toh ( m_SendIV ) ^ htobe16 ( len + 16 ) ) ;
LogPrint ( eLogDebug , " NTCP2: sent length " , len + 16 ) ;
// send message
2018-07-18 18:16:40 +03:00
m_IsSending = true ;
2018-06-25 19:28:07 +03:00
boost : : asio : : async_write ( m_Socket , boost : : asio : : buffer ( m_NextSendBuffer , len + 16 + 2 ) , boost : : asio : : transfer_all ( ) ,
std : : bind ( & NTCP2Session : : HandleNextFrameSent , shared_from_this ( ) , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
}
void NTCP2Session : : HandleNextFrameSent ( const boost : : system : : error_code & ecode , std : : size_t bytes_transferred )
{
2018-07-18 22:57:18 +03:00
m_NumSentBytes + = bytes_transferred ;
i2p : : transport : : transports . UpdateSentBytes ( bytes_transferred ) ;
2018-06-25 19:28:07 +03:00
delete [ ] m_NextSendBuffer ; m_NextSendBuffer = nullptr ;
LogPrint ( eLogDebug , " NTCP2: Next frame sent " ) ;
2018-07-18 18:16:40 +03:00
m_IsSending = false ;
SendQueue ( ) ;
}
void NTCP2Session : : SendQueue ( )
{
if ( ! m_SendQueue . empty ( ) )
{
uint8_t * payload = new uint8_t [ NTCP2_UNENCRYPTED_FRAME_MAX_SIZE ] ;
size_t s = 0 ;
// add I2NP blocks
while ( ! m_SendQueue . empty ( ) )
{
auto msg = m_SendQueue . front ( ) ;
size_t len = msg - > GetNTCP2Length ( ) ;
if ( s + len + 3 < = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE ) // 3 bytes block header
{
payload [ s ] = eNTCP2BlkI2NPMessage ; // blk
htobe16buf ( payload + s + 1 , len ) ; // size
s + = 3 ;
msg - > ToNTCP2 ( ) ;
memcpy ( payload + s , msg - > GetNTCP2Header ( ) , len ) ;
s + = len ;
m_SendQueue . pop_front ( ) ;
}
else
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 ;
RAND_bytes ( payload + s , paddingSize ) ;
s + = paddingSize ;
// send
SendNextFrame ( payload , s ) ;
delete [ ] payload ;
}
}
void NTCP2Session : : SendI2NPMessages ( const std : : vector < std : : shared_ptr < I2NPMessage > > & msgs )
{
2018-07-19 19:46:19 +03:00
m_Server . GetService ( ) . post ( std : : bind ( & NTCP2Session : : PostI2NPMessages , shared_from_this ( ) , msgs ) ) ;
}
void NTCP2Session : : PostI2NPMessages ( std : : vector < std : : shared_ptr < I2NPMessage > > msgs )
{
if ( m_IsTerminated ) return ;
2018-07-18 18:16:40 +03:00
for ( auto it : msgs )
m_SendQueue . push_back ( it ) ;
2018-07-19 19:46:19 +03:00
if ( ! m_IsSending )
SendQueue ( ) ;
2018-06-25 19:28:07 +03:00
}
2018-06-11 19:29:30 +03:00
NTCP2Server : : NTCP2Server ( ) :
m_IsRunning ( false ) , m_Thread ( nullptr ) , m_Work ( m_Service )
{
}
NTCP2Server : : ~ NTCP2Server ( )
{
Stop ( ) ;
}
void NTCP2Server : : Start ( )
{
if ( ! m_IsRunning )
{
m_IsRunning = true ;
m_Thread = new std : : thread ( std : : bind ( & NTCP2Server : : Run , this ) ) ;
2018-07-23 22:30:51 +03:00
auto & addresses = context . GetRouterInfo ( ) . GetAddresses ( ) ;
for ( const auto & address : addresses )
{
if ( ! address ) continue ;
if ( address - > IsPublishedNTCP2 ( ) )
{
if ( address - > host . is_v4 ( ) )
{
try
{
m_NTCP2Acceptor . reset ( new boost : : asio : : ip : : tcp : : acceptor ( m_Service , boost : : asio : : ip : : tcp : : endpoint ( boost : : asio : : ip : : tcp : : v4 ( ) , address - > port ) ) ) ;
}
catch ( std : : exception & ex )
{
LogPrint ( eLogError , " NTCP2: Failed to bind to ip4 port " , address - > port , ex . what ( ) ) ;
continue ;
}
2018-07-31 22:41:13 +03:00
LogPrint ( eLogInfo , " NTCP2: Start listening TCP port " , address - > port ) ;
2018-07-23 22:30:51 +03:00
auto conn = std : : make_shared < NTCP2Session > ( * this ) ;
m_NTCP2Acceptor - > async_accept ( conn - > GetSocket ( ) , std : : bind ( & NTCP2Server : : HandleAccept , this , conn , std : : placeholders : : _1 ) ) ;
}
else if ( address - > host . is_v6 ( ) & & context . SupportsV6 ( ) )
{
m_NTCP2V6Acceptor . reset ( new boost : : asio : : ip : : tcp : : acceptor ( m_Service ) ) ;
try
{
m_NTCP2V6Acceptor - > open ( boost : : asio : : ip : : tcp : : v6 ( ) ) ;
m_NTCP2V6Acceptor - > set_option ( boost : : asio : : ip : : v6_only ( true ) ) ;
m_NTCP2V6Acceptor - > bind ( boost : : asio : : ip : : tcp : : endpoint ( boost : : asio : : ip : : tcp : : v6 ( ) , address - > port ) ) ;
m_NTCP2V6Acceptor - > listen ( ) ;
LogPrint ( eLogInfo , " NTCP2: Start listening V6 TCP port " , address - > port ) ;
auto conn = std : : make_shared < NTCP2Session > ( * this ) ;
m_NTCP2V6Acceptor - > async_accept ( conn - > GetSocket ( ) , std : : bind ( & NTCP2Server : : HandleAcceptV6 , this , conn , std : : placeholders : : _1 ) ) ;
} catch ( std : : exception & ex ) {
LogPrint ( eLogError , " NTCP: failed to bind to ip6 port " , address - > port ) ;
continue ;
}
}
}
}
2018-06-11 19:29:30 +03:00
}
}
void NTCP2Server : : Stop ( )
{
2018-07-18 19:58:29 +03:00
{
// we have to copy it because Terminate changes m_NTCP2Sessions
auto ntcpSessions = m_NTCP2Sessions ;
for ( auto & it : ntcpSessions )
it . second - > Terminate ( ) ;
2018-07-23 22:30:51 +03:00
for ( auto & it : m_PendingIncomingSessions )
it - > Terminate ( ) ;
2018-07-18 19:58:29 +03:00
}
m_NTCP2Sessions . clear ( ) ;
2018-06-11 19:29:30 +03:00
if ( m_IsRunning )
{
m_IsRunning = false ;
m_Service . stop ( ) ;
if ( m_Thread )
{
m_Thread - > join ( ) ;
delete m_Thread ;
m_Thread = nullptr ;
}
}
}
void NTCP2Server : : Run ( )
{
while ( m_IsRunning )
{
try
{
m_Service . run ( ) ;
}
catch ( std : : exception & ex )
{
LogPrint ( eLogError , " NTCP2: runtime exception: " , ex . what ( ) ) ;
}
}
}
2018-07-18 19:58:29 +03:00
bool NTCP2Server : : AddNTCP2Session ( std : : shared_ptr < NTCP2Session > session )
{
if ( ! session | | ! session - > GetRemoteIdentity ( ) ) return false ;
auto & ident = session - > GetRemoteIdentity ( ) - > GetIdentHash ( ) ;
auto it = m_NTCP2Sessions . find ( ident ) ;
if ( it ! = m_NTCP2Sessions . end ( ) )
{
LogPrint ( eLogWarning , " NTCP2: session to " , ident . ToBase64 ( ) , " already exists " ) ;
session - > Terminate ( ) ;
return false ;
}
m_NTCP2Sessions . insert ( std : : make_pair ( ident , session ) ) ;
return true ;
}
void NTCP2Server : : RemoveNTCP2Session ( std : : shared_ptr < NTCP2Session > session )
{
if ( session & & session - > GetRemoteIdentity ( ) )
m_NTCP2Sessions . erase ( session - > GetRemoteIdentity ( ) - > GetIdentHash ( ) ) ;
}
std : : shared_ptr < NTCP2Session > NTCP2Server : : FindNTCP2Session ( const i2p : : data : : IdentHash & ident )
{
auto it = m_NTCP2Sessions . find ( ident ) ;
if ( it ! = m_NTCP2Sessions . end ( ) )
return it - > second ;
return nullptr ;
}
2018-06-11 19:29:30 +03:00
void NTCP2Server : : Connect ( const boost : : asio : : ip : : address & address , uint16_t port , std : : shared_ptr < NTCP2Session > conn )
{
2018-06-11 21:05:30 +03:00
LogPrint ( eLogDebug , " NTCP2: Connecting to " , address , " : " , port ) ;
2018-06-11 19:29:30 +03:00
m_Service . post ( [ this , address , port , conn ] ( )
{
2018-07-18 19:58:29 +03:00
if ( this - > AddNTCP2Session ( conn ) )
{
conn - > GetSocket ( ) . async_connect ( boost : : asio : : ip : : tcp : : endpoint ( address , port ) , std : : bind ( & NTCP2Server : : HandleConnect , this , std : : placeholders : : _1 , conn ) ) ;
}
2018-06-11 19:29:30 +03:00
} ) ;
}
void NTCP2Server : : HandleConnect ( const boost : : system : : error_code & ecode , std : : shared_ptr < NTCP2Session > conn )
{
if ( ecode )
{
LogPrint ( eLogInfo , " NTCP2: Connect error " , ecode . message ( ) ) ;
conn - > Terminate ( ) ;
}
else
{
LogPrint ( eLogDebug , " NTCP2: Connected to " , conn - > GetSocket ( ) . remote_endpoint ( ) ) ;
conn - > ClientLogin ( ) ;
}
}
2018-07-23 22:30:51 +03:00
void NTCP2Server : : HandleAccept ( std : : shared_ptr < NTCP2Session > conn , const boost : : system : : error_code & error )
{
if ( ! error )
{
boost : : system : : error_code ec ;
auto ep = conn - > GetSocket ( ) . remote_endpoint ( ec ) ;
if ( ! ec )
{
LogPrint ( eLogDebug , " NTCP2: Connected from " , ep ) ;
if ( conn )
{
conn - > ServerLogin ( ) ;
// m_PendingIncomingSessions.push_back (conn);
}
}
else
LogPrint ( eLogError , " NTCP2: Connected from error " , ec . message ( ) ) ;
}
if ( error ! = boost : : asio : : error : : operation_aborted )
{
conn = std : : make_shared < NTCP2Session > ( * this ) ;
m_NTCP2Acceptor - > async_accept ( conn - > GetSocket ( ) , std : : bind ( & NTCP2Server : : HandleAccept , this ,
conn , std : : placeholders : : _1 ) ) ;
}
}
void NTCP2Server : : HandleAcceptV6 ( std : : shared_ptr < NTCP2Session > conn , const boost : : system : : error_code & error )
{
if ( ! error )
{
boost : : system : : error_code ec ;
auto ep = conn - > GetSocket ( ) . remote_endpoint ( ec ) ;
if ( ! ec )
{
LogPrint ( eLogDebug , " NTCP2: Connected from " , ep ) ;
if ( conn )
{
conn - > ServerLogin ( ) ;
// m_PendingIncomingSessions.push_back (conn);
}
}
else
LogPrint ( eLogError , " NTCP2: Connected from error " , ec . message ( ) ) ;
}
if ( error ! = boost : : asio : : error : : operation_aborted )
{
conn = std : : make_shared < NTCP2Session > ( * this ) ;
m_NTCP2V6Acceptor - > async_accept ( conn - > GetSocket ( ) , std : : bind ( & NTCP2Server : : HandleAcceptV6 , this ,
conn , std : : placeholders : : _1 ) ) ;
}
}
2018-06-05 19:53:13 +03:00
}
}