2014-10-30 19:13:29 +00:00
# include <boost/bind.hpp>
2016-05-11 19:12:38 +00:00
# include "Crypto.h"
2014-10-30 19:13:29 +00:00
# include "Log.h"
# include "Timestamp.h"
# include "RouterContext.h"
# include "Transports.h"
# include "SSU.h"
# include "SSUSession.h"
namespace i2p
{
namespace transport
{
SSUSession : : SSUSession ( SSUServer & server , boost : : asio : : ip : : udp : : endpoint & remoteEndpoint ,
2016-08-04 14:26:50 +00:00
std : : shared_ptr < const i2p : : data : : RouterInfo > router , bool peerTest ) :
TransportSession ( router , SSU_TERMINATION_TIMEOUT ) ,
2016-08-24 15:21:49 +00:00
m_Server ( server ) , m_RemoteEndpoint ( remoteEndpoint ) , m_ConnectTimer ( GetService ( ) ) ,
2015-11-03 14:15:49 +00:00
m_IsPeerTest ( peerTest ) , m_State ( eSessionStateUnknown ) , m_IsSessionKey ( false ) ,
2016-12-30 03:06:33 +00:00
m_RelayTag ( 0 ) , m_SentRelayTag ( 0 ) , m_Data ( * this ) , m_IsDataReceived ( false )
2015-11-03 14:15:49 +00:00
{
if ( router )
{
// we are client
2016-06-27 15:08:23 +00:00
auto address = router - > GetSSUAddress ( false ) ;
2017-01-02 21:36:59 +00:00
if ( address ) m_IntroKey = address - > ssu - > key ;
2015-11-03 14:15:49 +00:00
m_Data . AdjustPacketSize ( router ) ; // mtu
}
else
{
// we are server
2016-06-27 15:08:23 +00:00
auto address = i2p : : context . GetRouterInfo ( ) . GetSSUAddress ( false ) ;
2017-01-02 21:36:59 +00:00
if ( address ) m_IntroKey = address - > ssu - > key ;
2015-11-03 14:15:49 +00:00
}
2014-10-30 19:13:29 +00:00
m_CreationTime = i2p : : util : : GetSecondsSinceEpoch ( ) ;
}
SSUSession : : ~ SSUSession ( )
{
}
2015-02-07 20:25:06 +00:00
boost : : asio : : io_service & SSUSession : : GetService ( )
{
return IsV6 ( ) ? m_Server . GetServiceV6 ( ) : m_Server . GetService ( ) ;
}
2014-10-30 19:13:29 +00:00
void SSUSession : : CreateAESandMacKey ( const uint8_t * pubKey )
{
uint8_t sharedKey [ 256 ] ;
2015-11-03 14:15:49 +00:00
m_DHKeysPair - > Agree ( pubKey , sharedKey ) ;
2014-10-30 19:13:29 +00:00
2014-11-01 18:56:13 +00:00
uint8_t * sessionKey = m_SessionKey , * macKey = m_MacKey ;
2014-10-30 19:13:29 +00:00
if ( sharedKey [ 0 ] & 0x80 )
{
2014-11-01 18:56:13 +00:00
sessionKey [ 0 ] = 0 ;
memcpy ( sessionKey + 1 , sharedKey , 31 ) ;
memcpy ( macKey , sharedKey + 31 , 32 ) ;
2014-10-30 19:13:29 +00:00
}
else if ( sharedKey [ 0 ] )
{
2014-11-01 18:56:13 +00:00
memcpy ( sessionKey , sharedKey , 32 ) ;
memcpy ( macKey , sharedKey + 32 , 32 ) ;
2014-10-30 19:13:29 +00:00
}
else
{
// find first non-zero byte
uint8_t * nonZero = sharedKey + 1 ;
while ( ! * nonZero )
{
nonZero + + ;
if ( nonZero - sharedKey > 32 )
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogWarning , " SSU: first 32 bytes of shared key is all zeros. Ignored " ) ;
2014-10-30 19:13:29 +00:00
return ;
}
}
2014-11-01 18:56:13 +00:00
memcpy ( sessionKey , nonZero , 32 ) ;
2015-11-03 14:15:49 +00:00
SHA256 ( nonZero , 64 - ( nonZero - sharedKey ) , macKey ) ;
2014-10-30 19:13:29 +00:00
}
m_IsSessionKey = true ;
m_SessionKeyEncryption . SetKey ( m_SessionKey ) ;
m_SessionKeyDecryption . SetKey ( m_SessionKey ) ;
}
void SSUSession : : ProcessNextMessage ( uint8_t * buf , size_t len , const boost : : asio : : ip : : udp : : endpoint & senderEndpoint )
{
m_NumReceivedBytes + = len ;
2015-03-16 23:33:59 +00:00
i2p : : transport : : transports . UpdateReceivedBytes ( len ) ;
2014-10-30 19:13:29 +00:00
if ( m_State = = eSessionStateIntroduced )
{
// HolePunch received
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: HolePunch of " , len , " bytes received " ) ;
2014-10-30 19:13:29 +00:00
m_State = eSessionStateUnknown ;
Connect ( ) ;
}
else
{
if ( ! len ) return ; // ignore zero-length packets
if ( m_State = = eSessionStateEstablished )
2016-08-24 15:21:49 +00:00
m_LastActivityTimestamp = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2014-10-30 19:13:29 +00:00
if ( m_IsSessionKey & & Validate ( buf , len , m_MacKey ) ) // try session key first
DecryptSessionKey ( buf , len ) ;
else
{
// try intro key depending on side
2015-11-03 14:15:49 +00:00
if ( Validate ( buf , len , m_IntroKey ) )
Decrypt ( buf , len , m_IntroKey ) ;
2014-10-30 19:13:29 +00:00
else
{
// try own intro key
2016-06-27 15:08:23 +00:00
auto address = i2p : : context . GetRouterInfo ( ) . GetSSUAddress ( false ) ;
2014-10-30 19:13:29 +00:00
if ( ! address )
{
2016-01-28 02:54:42 +00:00
LogPrint ( eLogInfo , " SSU is not supported " ) ;
2014-10-30 19:13:29 +00:00
return ;
}
2017-01-02 21:36:59 +00:00
if ( Validate ( buf , len , address - > ssu - > key ) )
Decrypt ( buf , len , address - > ssu - > key ) ;
2014-10-30 19:13:29 +00:00
else
{
2016-01-28 02:54:42 +00:00
LogPrint ( eLogWarning , " SSU: MAC verification failed " , len , " bytes from " , senderEndpoint ) ;
2014-11-24 17:26:11 +00:00
m_Server . DeleteSession ( shared_from_this ( ) ) ;
2014-10-30 19:13:29 +00:00
return ;
}
}
}
// successfully decrypted
ProcessMessage ( buf , len , senderEndpoint ) ;
}
}
2015-12-18 16:52:44 +00:00
size_t SSUSession : : GetSSUHeaderSize ( const uint8_t * buf ) const
2015-11-16 18:27:27 +00:00
{
size_t s = sizeof ( SSUHeader ) ;
2015-12-18 16:52:44 +00:00
if ( ( ( const SSUHeader * ) buf ) - > IsExtendedOptions ( ) )
2015-11-16 18:27:27 +00:00
s + = buf [ s ] + 1 ; // byte right after header is extended options length
return s ;
}
2014-10-30 19:13:29 +00:00
void SSUSession : : ProcessMessage ( uint8_t * buf , size_t len , const boost : : asio : : ip : : udp : : endpoint & senderEndpoint )
{
2015-03-30 17:10:36 +00:00
len - = ( len & 0x0F ) ; // %16, delete extra padding
if ( len < = sizeof ( SSUHeader ) ) return ; // drop empty message
2014-12-31 14:14:53 +00:00
//TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved
2015-11-16 18:27:27 +00:00
auto headerSize = GetSSUHeaderSize ( buf ) ;
2015-12-18 16:52:44 +00:00
if ( headerSize > = len )
{
LogPrint ( eLogError , " SSU header size " , headerSize , " exceeds packet length " , len ) ;
return ;
}
2014-10-30 19:13:29 +00:00
SSUHeader * header = ( SSUHeader * ) buf ;
switch ( header - > GetPayloadType ( ) )
{
case PAYLOAD_TYPE_DATA :
2015-11-16 18:27:27 +00:00
ProcessData ( buf + headerSize , len - headerSize ) ;
2014-10-30 19:13:29 +00:00
break ;
case PAYLOAD_TYPE_SESSION_REQUEST :
2016-02-23 17:16:53 +00:00
ProcessSessionRequest ( buf , len , senderEndpoint ) ; // buf with header
2014-10-30 19:13:29 +00:00
break ;
case PAYLOAD_TYPE_SESSION_CREATED :
2015-12-18 16:52:44 +00:00
ProcessSessionCreated ( buf , len ) ; // buf with header
2014-10-30 19:13:29 +00:00
break ;
case PAYLOAD_TYPE_SESSION_CONFIRMED :
2015-12-18 16:52:44 +00:00
ProcessSessionConfirmed ( buf , len ) ; // buf with header
2014-10-30 19:13:29 +00:00
break ;
case PAYLOAD_TYPE_PEER_TEST :
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: peer test received " ) ;
2015-11-16 18:27:27 +00:00
ProcessPeerTest ( buf + headerSize , len - headerSize , senderEndpoint ) ;
2014-10-30 19:13:29 +00:00
break ;
case PAYLOAD_TYPE_SESSION_DESTROYED :
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: session destroy received " ) ;
2014-11-24 17:26:11 +00:00
m_Server . DeleteSession ( shared_from_this ( ) ) ;
2014-10-30 19:13:29 +00:00
break ;
}
case PAYLOAD_TYPE_RELAY_RESPONSE :
2015-12-18 16:52:44 +00:00
ProcessRelayResponse ( buf + headerSize , len - headerSize ) ;
2014-10-30 19:13:29 +00:00
if ( m_State ! = eSessionStateEstablished )
2014-11-24 17:26:11 +00:00
m_Server . DeleteSession ( shared_from_this ( ) ) ;
2014-10-30 19:13:29 +00:00
break ;
case PAYLOAD_TYPE_RELAY_REQUEST :
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: relay request received " ) ;
2015-11-16 18:27:27 +00:00
ProcessRelayRequest ( buf + headerSize , len - headerSize , senderEndpoint ) ;
2014-10-30 19:13:29 +00:00
break ;
case PAYLOAD_TYPE_RELAY_INTRO :
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: relay intro received " ) ;
2015-11-16 18:27:27 +00:00
ProcessRelayIntro ( buf + headerSize , len - headerSize ) ;
2014-10-30 19:13:29 +00:00
break ;
default :
2015-12-17 08:09:54 +00:00
LogPrint ( eLogWarning , " SSU: Unexpected payload type " , ( int ) header - > GetPayloadType ( ) ) ;
2014-10-30 19:13:29 +00:00
}
}
2015-12-18 16:52:44 +00:00
void SSUSession : : ProcessSessionRequest ( const uint8_t * buf , size_t len , const boost : : asio : : ip : : udp : : endpoint & senderEndpoint )
2014-10-30 19:13:29 +00:00
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU message: session request " ) ;
2016-02-23 17:16:53 +00:00
bool sendRelayTag = true ;
auto headerSize = sizeof ( SSUHeader ) ;
if ( ( ( SSUHeader * ) buf ) - > IsExtendedOptions ( ) )
{
uint8_t extendedOptionsLen = buf [ headerSize ] ;
headerSize + + ;
if ( extendedOptionsLen > = 3 ) // options are presented
{
uint16_t flags = bufbe16toh ( buf + headerSize ) ;
sendRelayTag = flags & EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG ;
}
headerSize + = extendedOptionsLen ;
}
if ( headerSize > = len )
{
LogPrint ( eLogError , " Session reaquest header size " , headerSize , " exceeds packet length " , len ) ;
return ;
}
2014-10-30 19:13:29 +00:00
m_RemoteEndpoint = senderEndpoint ;
if ( ! m_DHKeysPair )
m_DHKeysPair = transports . GetNextDHKeysPair ( ) ;
2016-02-23 17:16:53 +00:00
CreateAESandMacKey ( buf + headerSize ) ;
SendSessionCreated ( buf + headerSize , sendRelayTag ) ;
2014-10-30 19:13:29 +00:00
}
void SSUSession : : ProcessSessionCreated ( uint8_t * buf , size_t len )
{
2015-11-03 14:15:49 +00:00
if ( ! IsOutgoing ( ) | | ! m_DHKeysPair )
2014-10-30 19:13:29 +00:00
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogWarning , " SSU: Unsolicited session created message " ) ;
2014-10-30 19:13:29 +00:00
return ;
}
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU message: session created " ) ;
2016-08-24 15:21:49 +00:00
m_ConnectTimer . cancel ( ) ; // connect timer
2014-10-30 19:13:29 +00:00
SignedData s ; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time
2015-12-18 16:52:44 +00:00
auto headerSize = GetSSUHeaderSize ( buf ) ;
if ( headerSize > = len )
{
LogPrint ( eLogError , " Session created header size " , headerSize , " exceeds packet length " , len ) ;
return ;
}
uint8_t * payload = buf + headerSize ;
2014-10-30 19:13:29 +00:00
uint8_t * y = payload ;
CreateAESandMacKey ( y ) ;
2015-11-03 14:15:49 +00:00
s . Insert ( m_DHKeysPair - > GetPublicKey ( ) , 256 ) ; // x
2014-10-30 19:13:29 +00:00
s . Insert ( y , 256 ) ; // y
payload + = 256 ;
uint8_t addressSize = * payload ;
payload + = 1 ; // size
uint8_t * ourAddress = payload ;
boost : : asio : : ip : : address ourIP ;
if ( addressSize = = 4 ) // v4
{
boost : : asio : : ip : : address_v4 : : bytes_type bytes ;
memcpy ( bytes . data ( ) , ourAddress , 4 ) ;
ourIP = boost : : asio : : ip : : address_v4 ( bytes ) ;
}
else // v6
{
boost : : asio : : ip : : address_v6 : : bytes_type bytes ;
memcpy ( bytes . data ( ) , ourAddress , 16 ) ;
ourIP = boost : : asio : : ip : : address_v6 ( bytes ) ;
}
s . Insert ( ourAddress , addressSize ) ; // our IP
payload + = addressSize ; // address
2014-12-29 22:04:02 +00:00
uint16_t ourPort = bufbe16toh ( payload ) ;
2014-10-30 19:13:29 +00:00
s . Insert ( payload , 2 ) ; // our port
payload + = 2 ; // port
if ( m_RemoteEndpoint . address ( ) . is_v4 ( ) )
s . Insert ( m_RemoteEndpoint . address ( ) . to_v4 ( ) . to_bytes ( ) . data ( ) , 4 ) ; // remote IP v4
else
s . Insert ( m_RemoteEndpoint . address ( ) . to_v6 ( ) . to_bytes ( ) . data ( ) , 16 ) ; // remote IP v6
2015-02-25 03:17:46 +00:00
s . Insert < uint16_t > ( htobe16 ( m_RemoteEndpoint . port ( ) ) ) ; // remote port
2014-10-30 19:13:29 +00:00
s . Insert ( payload , 8 ) ; // relayTag and signed on time
2014-12-29 22:04:02 +00:00
m_RelayTag = bufbe32toh ( payload ) ;
2014-10-30 19:13:29 +00:00
payload + = 4 ; // relayTag
2016-09-18 22:42:21 +00:00
if ( i2p : : context . GetStatus ( ) = = eRouterStatusTesting )
{
auto ts = i2p : : util : : GetSecondsSinceEpoch ( ) ;
uint32_t signedOnTime = bufbe32toh ( payload ) ;
if ( signedOnTime < ts - SSU_CLOCK_SKEW | | signedOnTime > ts + SSU_CLOCK_SKEW )
{
LogPrint ( eLogError , " SSU: clock skew detected " , ( int ) ts - signedOnTime , " . Check your clock " ) ;
2016-09-20 01:37:04 +00:00
i2p : : context . SetError ( eRouterErrorClockSkew ) ;
2016-09-18 22:42:21 +00:00
}
}
2014-10-30 19:13:29 +00:00
payload + = 4 ; // signed on time
// decrypt signature
2015-11-03 14:15:49 +00:00
size_t signatureLen = m_RemoteIdentity - > GetSignatureLen ( ) ;
2014-10-30 19:13:29 +00:00
size_t paddingSize = signatureLen & 0x0F ; // %16
if ( paddingSize > 0 ) signatureLen + = ( 16 - paddingSize ) ;
2014-12-31 14:14:53 +00:00
//TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved
2014-10-30 19:13:29 +00:00
m_SessionKeyDecryption . SetIV ( ( ( SSUHeader * ) buf ) - > iv ) ;
2015-12-18 16:52:44 +00:00
m_SessionKeyDecryption . Decrypt ( payload , signatureLen , payload ) ; // TODO: non-const payload
2016-05-10 19:55:48 +00:00
// verify signature
if ( s . Verify ( m_RemoteIdentity , payload ) )
{
LogPrint ( eLogInfo , " SSU: Our external address is " , ourIP . to_string ( ) , " : " , ourPort ) ;
i2p : : context . UpdateAddress ( ourIP ) ;
SendSessionConfirmed ( y , ourAddress , addressSize + 2 ) ;
}
else
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogError , " SSU: message 'created' signature verification failed " ) ;
2016-05-10 19:55:48 +00:00
Failed ( ) ;
}
2014-10-30 19:13:29 +00:00
}
2015-12-18 16:52:44 +00:00
void SSUSession : : ProcessSessionConfirmed ( const uint8_t * buf , size_t len )
2014-10-30 19:13:29 +00:00
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: Session confirmed received " ) ;
2015-12-18 16:52:44 +00:00
auto headerSize = GetSSUHeaderSize ( buf ) ;
if ( headerSize > = len )
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogError , " SSU: Session confirmed header size " , len , " exceeds packet length " , len ) ;
2015-12-18 16:52:44 +00:00
return ;
}
const uint8_t * payload = buf + headerSize ;
2014-10-30 19:13:29 +00:00
payload + + ; // identity fragment info
2014-12-29 22:04:02 +00:00
uint16_t identitySize = bufbe16toh ( payload ) ;
2014-10-30 19:13:29 +00:00
payload + = 2 ; // size of identity fragment
2015-11-03 14:15:49 +00:00
SetRemoteIdentity ( std : : make_shared < i2p : : data : : IdentityEx > ( payload , identitySize ) ) ;
m_Data . UpdatePacketSize ( m_RemoteIdentity - > GetIdentHash ( ) ) ;
2014-10-30 19:13:29 +00:00
payload + = identitySize ; // identity
2016-09-18 22:42:21 +00:00
auto ts = i2p : : util : : GetSecondsSinceEpoch ( ) ;
uint32_t signedOnTime = bufbe32toh ( payload ) ;
if ( signedOnTime < ts - SSU_CLOCK_SKEW | | signedOnTime > ts + SSU_CLOCK_SKEW )
{
LogPrint ( eLogError , " SSU message 'confirmed' time difference " , ( int ) ts - signedOnTime , " exceeds clock skew " ) ;
Failed ( ) ;
return ;
}
2015-11-03 14:15:49 +00:00
if ( m_SignedData )
m_SignedData - > Insert ( payload , 4 ) ; // insert Alice's signed on time
2014-10-30 19:13:29 +00:00
payload + = 4 ; // signed-on time
2015-11-03 14:15:49 +00:00
size_t paddingSize = ( payload - buf ) + m_RemoteIdentity - > GetSignatureLen ( ) ;
2014-10-30 19:13:29 +00:00
paddingSize & = 0x0F ; // %16
if ( paddingSize > 0 ) paddingSize = 16 - paddingSize ;
payload + = paddingSize ;
2016-05-10 19:55:48 +00:00
// verify signature
if ( m_SignedData & & m_SignedData - > Verify ( m_RemoteIdentity , payload ) )
{
m_Data . Send ( CreateDeliveryStatusMsg ( 0 ) ) ;
Established ( ) ;
}
else
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogError , " SSU message 'confirmed' signature verification failed " ) ;
2016-05-10 19:55:48 +00:00
Failed ( ) ;
}
2014-10-30 19:13:29 +00:00
}
void SSUSession : : SendSessionRequest ( )
2015-11-03 14:15:49 +00:00
{
2014-10-30 19:13:29 +00:00
uint8_t buf [ 320 + 18 ] ; // 304 bytes for ipv4, 320 for ipv6
uint8_t * payload = buf + sizeof ( SSUHeader ) ;
2016-02-25 23:40:40 +00:00
uint8_t flag = 0 ;
2016-02-25 20:57:58 +00:00
// fill extended options, 3 bytes extended options don't change message size
if ( i2p : : context . GetStatus ( ) = = eRouterStatusOK ) // we don't need relays
{
// tell out peer to now assign relay tag
2016-02-25 23:40:40 +00:00
flag = SSU_HEADER_EXTENDED_OPTIONS_INCLUDED ;
2016-02-25 20:57:58 +00:00
* payload = 2 ; payload + + ; // 1 byte length
uint16_t flags = 0 ; // clear EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG
htobe16buf ( payload , flags ) ;
payload + = 2 ;
2016-02-25 23:40:40 +00:00
}
2016-02-25 20:57:58 +00:00
// fill payload
2015-11-03 14:15:49 +00:00
memcpy ( payload , m_DHKeysPair - > GetPublicKey ( ) , 256 ) ; // x
2014-10-30 19:13:29 +00:00
bool isV4 = m_RemoteEndpoint . address ( ) . is_v4 ( ) ;
if ( isV4 )
{
payload [ 256 ] = 4 ;
memcpy ( payload + 257 , m_RemoteEndpoint . address ( ) . to_v4 ( ) . to_bytes ( ) . data ( ) , 4 ) ;
}
else
{
payload [ 256 ] = 16 ;
memcpy ( payload + 257 , m_RemoteEndpoint . address ( ) . to_v6 ( ) . to_bytes ( ) . data ( ) , 16 ) ;
}
2016-02-25 20:57:58 +00:00
// encrypt and send
2014-10-30 19:13:29 +00:00
uint8_t iv [ 16 ] ;
2015-11-03 14:15:49 +00:00
RAND_bytes ( iv , 16 ) ; // random iv
2016-02-25 23:40:40 +00:00
FillHeaderAndEncrypt ( PAYLOAD_TYPE_SESSION_REQUEST , buf , isV4 ? 304 : 320 , m_IntroKey , iv , m_IntroKey , flag ) ;
2014-10-30 19:13:29 +00:00
m_Server . Send ( buf , isV4 ? 304 : 320 , m_RemoteEndpoint ) ;
}
2015-12-10 03:17:43 +00:00
void SSUSession : : SendRelayRequest ( const i2p : : data : : RouterInfo : : Introducer & introducer , uint32_t nonce )
2014-10-30 19:13:29 +00:00
{
2016-06-27 15:08:23 +00:00
auto address = i2p : : context . GetRouterInfo ( ) . GetSSUAddress ( false ) ;
2014-10-30 19:13:29 +00:00
if ( ! address )
{
2016-01-28 02:54:42 +00:00
LogPrint ( eLogInfo , " SSU is not supported " ) ;
2014-10-30 19:13:29 +00:00
return ;
}
uint8_t buf [ 96 + 18 ] ;
uint8_t * payload = buf + sizeof ( SSUHeader ) ;
2015-11-03 14:15:49 +00:00
htobe32buf ( payload , introducer . iTag ) ;
2014-10-30 19:13:29 +00:00
payload + = 4 ;
* payload = 0 ; // no address
payload + + ;
2014-12-30 14:37:24 +00:00
htobuf16 ( payload , 0 ) ; // port = 0
2014-10-30 19:13:29 +00:00
payload + = 2 ;
* payload = 0 ; // challenge
payload + + ;
2017-01-02 21:36:59 +00:00
memcpy ( payload , ( const uint8_t * ) address - > ssu - > key , 32 ) ;
2014-10-30 19:13:29 +00:00
payload + = 32 ;
2015-12-10 03:17:43 +00:00
htobe32buf ( payload , nonce ) ; // nonce
2014-10-30 19:13:29 +00:00
uint8_t iv [ 16 ] ;
2015-11-03 14:15:49 +00:00
RAND_bytes ( iv , 16 ) ; // random iv
2014-10-30 19:13:29 +00:00
if ( m_State = = eSessionStateEstablished )
FillHeaderAndEncrypt ( PAYLOAD_TYPE_RELAY_REQUEST , buf , 96 , m_SessionKey , iv , m_MacKey ) ;
else
2015-11-03 14:15:49 +00:00
FillHeaderAndEncrypt ( PAYLOAD_TYPE_RELAY_REQUEST , buf , 96 , introducer . iKey , iv , introducer . iKey ) ;
2014-10-30 19:13:29 +00:00
m_Server . Send ( buf , 96 , m_RemoteEndpoint ) ;
}
2016-02-23 17:16:53 +00:00
void SSUSession : : SendSessionCreated ( const uint8_t * x , bool sendRelayTag )
2014-10-30 19:13:29 +00:00
{
auto address = IsV6 ( ) ? i2p : : context . GetRouterInfo ( ) . GetSSUV6Address ( ) :
i2p : : context . GetRouterInfo ( ) . GetSSUAddress ( true ) ; //v4 only
2015-11-03 14:15:49 +00:00
if ( ! address )
2014-10-30 19:13:29 +00:00
{
2016-01-28 02:54:42 +00:00
LogPrint ( eLogInfo , " SSU is not supported " ) ;
2014-10-30 19:13:29 +00:00
return ;
}
SignedData s ; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time
s . Insert ( x , 256 ) ; // x
uint8_t buf [ 384 + 18 ] ;
uint8_t * payload = buf + sizeof ( SSUHeader ) ;
2015-11-03 14:15:49 +00:00
memcpy ( payload , m_DHKeysPair - > GetPublicKey ( ) , 256 ) ;
2014-10-30 19:13:29 +00:00
s . Insert ( payload , 256 ) ; // y
payload + = 256 ;
if ( m_RemoteEndpoint . address ( ) . is_v4 ( ) )
{
// ipv4
* payload = 4 ;
payload + + ;
memcpy ( payload , m_RemoteEndpoint . address ( ) . to_v4 ( ) . to_bytes ( ) . data ( ) , 4 ) ;
s . Insert ( payload , 4 ) ; // remote endpoint IP V4
payload + = 4 ;
}
else
{
// ipv6
* payload = 16 ;
payload + + ;
memcpy ( payload , m_RemoteEndpoint . address ( ) . to_v6 ( ) . to_bytes ( ) . data ( ) , 16 ) ;
s . Insert ( payload , 16 ) ; // remote endpoint IP V6
payload + = 16 ;
}
2014-12-30 14:37:24 +00:00
htobe16buf ( payload , m_RemoteEndpoint . port ( ) ) ;
2014-10-30 19:13:29 +00:00
s . Insert ( payload , 2 ) ; // remote port
payload + = 2 ;
if ( address - > host . is_v4 ( ) )
s . Insert ( address - > host . to_v4 ( ) . to_bytes ( ) . data ( ) , 4 ) ; // our IP V4
else
s . Insert ( address - > host . to_v6 ( ) . to_bytes ( ) . data ( ) , 16 ) ; // our IP V6
2015-02-25 03:17:46 +00:00
s . Insert < uint16_t > ( htobe16 ( address - > port ) ) ; // our port
2016-02-23 17:16:53 +00:00
if ( sendRelayTag & & i2p : : context . GetRouterInfo ( ) . IsIntroducer ( ) & & ! IsV6 ( ) )
2014-10-30 19:13:29 +00:00
{
2016-12-30 03:06:33 +00:00
RAND_bytes ( ( uint8_t * ) & m_SentRelayTag , 4 ) ;
if ( ! m_SentRelayTag ) m_SentRelayTag = 1 ;
2014-10-30 19:13:29 +00:00
}
2016-12-30 03:06:33 +00:00
htobe32buf ( payload , m_SentRelayTag ) ;
2014-10-30 19:13:29 +00:00
payload + = 4 ; // relay tag
2014-12-30 14:37:24 +00:00
htobe32buf ( payload , i2p : : util : : GetSecondsSinceEpoch ( ) ) ; // signed on time
2014-10-30 19:13:29 +00:00
payload + = 4 ;
2015-11-03 14:15:49 +00:00
s . Insert ( payload - 8 , 4 ) ; // relayTag
// we have to store this signed data for session confirmed
// same data but signed on time, it will Alice's there
m_SignedData = std : : unique_ptr < SignedData > ( new SignedData ( s ) ) ;
s . Insert ( payload - 4 , 4 ) ; // BOB's signed on time
2014-10-30 19:13:29 +00:00
s . Sign ( i2p : : context . GetPrivateKeys ( ) , payload ) ; // DSA signature
// TODO: fill padding with random data
uint8_t iv [ 16 ] ;
2015-11-03 14:15:49 +00:00
RAND_bytes ( iv , 16 ) ; // random iv
2014-10-30 19:13:29 +00:00
// encrypt signature and padding with newly created session key
2015-11-03 14:15:49 +00:00
size_t signatureLen = i2p : : context . GetIdentity ( ) - > GetSignatureLen ( ) ;
2014-10-30 19:13:29 +00:00
size_t paddingSize = signatureLen & 0x0F ; // %16
if ( paddingSize > 0 ) signatureLen + = ( 16 - paddingSize ) ;
m_SessionKeyEncryption . SetIV ( iv ) ;
m_SessionKeyEncryption . Encrypt ( payload , signatureLen , payload ) ;
payload + = signatureLen ;
size_t msgLen = payload - buf ;
// encrypt message with intro key
2015-11-03 14:15:49 +00:00
FillHeaderAndEncrypt ( PAYLOAD_TYPE_SESSION_CREATED , buf , msgLen , m_IntroKey , iv , m_IntroKey ) ;
2014-10-30 19:13:29 +00:00
Send ( buf , msgLen ) ;
}
void SSUSession : : SendSessionConfirmed ( const uint8_t * y , const uint8_t * ourAddress , size_t ourAddressLen )
{
uint8_t buf [ 512 + 18 ] ;
uint8_t * payload = buf + sizeof ( SSUHeader ) ;
* payload = 1 ; // 1 fragment
payload + + ; // info
2015-11-03 14:15:49 +00:00
size_t identLen = i2p : : context . GetIdentity ( ) - > GetFullLen ( ) ; // 387+ bytes
2014-12-30 14:37:24 +00:00
htobe16buf ( payload , identLen ) ;
2014-10-30 19:13:29 +00:00
payload + = 2 ; // cursize
2015-11-03 14:15:49 +00:00
i2p : : context . GetIdentity ( ) - > ToBuffer ( payload , identLen ) ;
2014-10-30 19:13:29 +00:00
payload + = identLen ;
uint32_t signedOnTime = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2014-12-30 14:37:24 +00:00
htobe32buf ( payload , signedOnTime ) ; // signed on time
2014-10-30 19:13:29 +00:00
payload + = 4 ;
2015-11-03 14:15:49 +00:00
auto signatureLen = i2p : : context . GetIdentity ( ) - > GetSignatureLen ( ) ;
2014-10-30 19:13:29 +00:00
size_t paddingSize = ( ( payload - buf ) + signatureLen ) % 16 ;
if ( paddingSize > 0 ) paddingSize = 16 - paddingSize ;
// TODO: fill padding
payload + = paddingSize ; // padding size
// signature
SignedData s ; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time
2015-11-03 14:15:49 +00:00
s . Insert ( m_DHKeysPair - > GetPublicKey ( ) , 256 ) ; // x
2014-10-30 19:13:29 +00:00
s . Insert ( y , 256 ) ; // y
s . Insert ( ourAddress , ourAddressLen ) ; // our address/port as seem by party
if ( m_RemoteEndpoint . address ( ) . is_v4 ( ) )
s . Insert ( m_RemoteEndpoint . address ( ) . to_v4 ( ) . to_bytes ( ) . data ( ) , 4 ) ; // remote IP V4
else
s . Insert ( m_RemoteEndpoint . address ( ) . to_v6 ( ) . to_bytes ( ) . data ( ) , 16 ) ; // remote IP V6
2015-02-25 03:17:46 +00:00
s . Insert < uint16_t > ( htobe16 ( m_RemoteEndpoint . port ( ) ) ) ; // remote port
2014-10-30 19:13:29 +00:00
s . Insert ( htobe32 ( m_RelayTag ) ) ; // relay tag
s . Insert ( htobe32 ( signedOnTime ) ) ; // signed on time
s . Sign ( i2p : : context . GetPrivateKeys ( ) , payload ) ; // DSA signature
payload + = signatureLen ;
size_t msgLen = payload - buf ;
uint8_t iv [ 16 ] ;
2015-11-03 14:15:49 +00:00
RAND_bytes ( iv , 16 ) ; // random iv
2014-10-30 19:13:29 +00:00
// encrypt message with session key
FillHeaderAndEncrypt ( PAYLOAD_TYPE_SESSION_CONFIRMED , buf , msgLen , m_SessionKey , iv , m_MacKey ) ;
Send ( buf , msgLen ) ;
}
2015-12-18 16:52:44 +00:00
void SSUSession : : ProcessRelayRequest ( const uint8_t * buf , size_t len , const boost : : asio : : ip : : udp : : endpoint & from )
2014-10-30 19:13:29 +00:00
{
2014-12-29 22:04:02 +00:00
uint32_t relayTag = bufbe32toh ( buf ) ;
2014-10-30 19:13:29 +00:00
auto session = m_Server . FindRelaySession ( relayTag ) ;
if ( session )
{
buf + = 4 ; // relay tag
uint8_t size = * buf ;
buf + + ; // size
buf + = size ; // address
buf + = 2 ; // port
uint8_t challengeSize = * buf ;
buf + + ; // challenge size
buf + = challengeSize ;
2015-12-18 16:52:44 +00:00
const uint8_t * introKey = buf ;
2014-10-30 19:13:29 +00:00
buf + = 32 ; // introkey
2014-12-29 22:04:02 +00:00
uint32_t nonce = bufbe32toh ( buf ) ;
2014-10-30 19:13:29 +00:00
SendRelayResponse ( nonce , from , introKey , session - > m_RemoteEndpoint ) ;
2015-11-30 15:23:05 +00:00
SendRelayIntro ( session , from ) ;
2014-10-30 19:13:29 +00:00
}
}
void SSUSession : : SendRelayResponse ( uint32_t nonce , const boost : : asio : : ip : : udp : : endpoint & from ,
const uint8_t * introKey , const boost : : asio : : ip : : udp : : endpoint & to )
{
uint8_t buf [ 80 + 18 ] ; // 64 Alice's ipv4 and 80 Alice's ipv6
uint8_t * payload = buf + sizeof ( SSUHeader ) ;
// Charlie's address always v4
if ( ! to . address ( ) . is_v4 ( ) )
{
2016-01-28 02:54:42 +00:00
LogPrint ( eLogWarning , " SSU: Charlie's IP must be v4 " ) ;
2014-10-30 19:13:29 +00:00
return ;
}
* payload = 4 ;
payload + + ; // size
2014-12-30 14:37:24 +00:00
htobe32buf ( payload , to . address ( ) . to_v4 ( ) . to_ulong ( ) ) ; // Charlie's IP
2014-10-30 19:13:29 +00:00
payload + = 4 ; // address
2014-12-30 14:37:24 +00:00
htobe16buf ( payload , to . port ( ) ) ; // Charlie's port
2014-10-30 19:13:29 +00:00
payload + = 2 ; // port
// Alice
bool isV4 = from . address ( ) . is_v4 ( ) ; // Alice's
if ( isV4 )
{
* payload = 4 ;
payload + + ; // size
memcpy ( payload , from . address ( ) . to_v4 ( ) . to_bytes ( ) . data ( ) , 4 ) ; // Alice's IP V4
payload + = 4 ; // address
}
else
{
* payload = 16 ;
payload + + ; // size
memcpy ( payload , from . address ( ) . to_v6 ( ) . to_bytes ( ) . data ( ) , 16 ) ; // Alice's IP V6
payload + = 16 ; // address
}
2014-12-30 14:37:24 +00:00
htobe16buf ( payload , from . port ( ) ) ; // Alice's port
2014-10-30 19:13:29 +00:00
payload + = 2 ; // port
2014-12-30 14:37:24 +00:00
htobe32buf ( payload , nonce ) ;
2014-10-30 19:13:29 +00:00
if ( m_State = = eSessionStateEstablished )
{
// encrypt with session key
FillHeaderAndEncrypt ( PAYLOAD_TYPE_RELAY_RESPONSE , buf , isV4 ? 64 : 80 ) ;
Send ( buf , isV4 ? 64 : 80 ) ;
}
else
{
// ecrypt with Alice's intro key
uint8_t iv [ 16 ] ;
2015-11-03 14:15:49 +00:00
RAND_bytes ( iv , 16 ) ; // random iv
2014-10-30 19:13:29 +00:00
FillHeaderAndEncrypt ( PAYLOAD_TYPE_RELAY_RESPONSE , buf , isV4 ? 64 : 80 , introKey , iv , introKey ) ;
m_Server . Send ( buf , isV4 ? 64 : 80 , from ) ;
}
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: relay response sent " ) ;
2014-10-30 19:13:29 +00:00
}
2015-11-30 15:23:05 +00:00
void SSUSession : : SendRelayIntro ( std : : shared_ptr < SSUSession > session , const boost : : asio : : ip : : udp : : endpoint & from )
2014-10-30 19:13:29 +00:00
{
if ( ! session ) return ;
// Alice's address always v4
if ( ! from . address ( ) . is_v4 ( ) )
{
2016-01-28 02:54:42 +00:00
LogPrint ( eLogWarning , " SSU: Alice's IP must be v4 " ) ;
2014-10-30 19:13:29 +00:00
return ;
}
uint8_t buf [ 48 + 18 ] ;
uint8_t * payload = buf + sizeof ( SSUHeader ) ;
* payload = 4 ;
payload + + ; // size
2014-12-30 14:37:24 +00:00
htobe32buf ( payload , from . address ( ) . to_v4 ( ) . to_ulong ( ) ) ; // Alice's IP
2014-10-30 19:13:29 +00:00
payload + = 4 ; // address
2014-12-30 14:37:24 +00:00
htobe16buf ( payload , from . port ( ) ) ; // Alice's port
2014-10-30 19:13:29 +00:00
payload + = 2 ; // port
* payload = 0 ; // challenge size
uint8_t iv [ 16 ] ;
2015-11-03 14:15:49 +00:00
RAND_bytes ( iv , 16 ) ; // random iv
2014-10-30 19:13:29 +00:00
FillHeaderAndEncrypt ( PAYLOAD_TYPE_RELAY_INTRO , buf , 48 , session - > m_SessionKey , iv , session - > m_MacKey ) ;
m_Server . Send ( buf , 48 , session - > m_RemoteEndpoint ) ;
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: relay intro sent " ) ;
2014-10-30 19:13:29 +00:00
}
2015-12-18 16:52:44 +00:00
void SSUSession : : ProcessRelayResponse ( const uint8_t * buf , size_t len )
2014-10-30 19:13:29 +00:00
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU message: Relay response received " ) ;
2015-12-18 16:52:44 +00:00
uint8_t remoteSize = * buf ;
buf + + ; // remote size
boost : : asio : : ip : : address_v4 remoteIP ( bufbe32toh ( buf ) ) ;
buf + = remoteSize ; // remote address
uint16_t remotePort = bufbe16toh ( buf ) ;
buf + = 2 ; // remote port
uint8_t ourSize = * buf ;
buf + + ; // our size
2014-10-30 19:13:29 +00:00
boost : : asio : : ip : : address ourIP ;
if ( ourSize = = 4 )
{
boost : : asio : : ip : : address_v4 : : bytes_type bytes ;
2015-12-18 16:52:44 +00:00
memcpy ( bytes . data ( ) , buf , 4 ) ;
2014-10-30 19:13:29 +00:00
ourIP = boost : : asio : : ip : : address_v4 ( bytes ) ;
}
else
{
boost : : asio : : ip : : address_v6 : : bytes_type bytes ;
2015-12-18 16:52:44 +00:00
memcpy ( bytes . data ( ) , buf , 16 ) ;
2014-10-30 19:13:29 +00:00
ourIP = boost : : asio : : ip : : address_v6 ( bytes ) ;
}
2015-12-18 16:52:44 +00:00
buf + = ourSize ; // our address
uint16_t ourPort = bufbe16toh ( buf ) ;
buf + = 2 ; // our port
2015-12-17 08:09:54 +00:00
LogPrint ( eLogInfo , " SSU: Our external address is " , ourIP . to_string ( ) , " : " , ourPort ) ;
2014-10-30 19:13:29 +00:00
i2p : : context . UpdateAddress ( ourIP ) ;
2015-12-18 16:52:44 +00:00
uint32_t nonce = bufbe32toh ( buf ) ;
buf + = 4 ; // nonce
2015-12-10 03:17:43 +00:00
auto it = m_RelayRequests . find ( nonce ) ;
if ( it ! = m_RelayRequests . end ( ) )
{
// check if we are waiting for introduction
boost : : asio : : ip : : udp : : endpoint remoteEndpoint ( remoteIP , remotePort ) ;
if ( ! m_Server . FindSession ( remoteEndpoint ) )
{
// we didn't have correct endpoint when sent relay request
// now we do
2015-12-17 08:09:54 +00:00
LogPrint ( eLogInfo , " SSU: RelayReponse connecting to endpoint " , remoteEndpoint ) ;
2015-12-10 03:17:43 +00:00
if ( i2p : : context . GetRouterInfo ( ) . UsesIntroducer ( ) ) // if we are unreachable
m_Server . Send ( buf , 0 , remoteEndpoint ) ; // send HolePunch
m_Server . CreateDirectSession ( it - > second , remoteEndpoint , false ) ;
}
// delete request
m_RelayRequests . erase ( it ) ;
}
else
2015-12-17 08:09:54 +00:00
LogPrint ( eLogError , " SSU: Unsolicited RelayResponse, nonce= " , nonce ) ;
2014-10-30 19:13:29 +00:00
}
2015-12-18 16:52:44 +00:00
void SSUSession : : ProcessRelayIntro ( const uint8_t * buf , size_t len )
2014-10-30 19:13:29 +00:00
{
uint8_t size = * buf ;
if ( size = = 4 )
{
buf + + ; // size
2014-12-29 22:04:02 +00:00
boost : : asio : : ip : : address_v4 address ( bufbe32toh ( buf ) ) ;
2014-10-30 19:13:29 +00:00
buf + = 4 ; // address
2014-12-29 22:04:02 +00:00
uint16_t port = bufbe16toh ( buf ) ;
2015-12-18 16:52:44 +00:00
// send hole punch of 0 bytes
2014-10-30 19:13:29 +00:00
m_Server . Send ( buf , 0 , boost : : asio : : ip : : udp : : endpoint ( address , port ) ) ;
}
else
2015-12-17 08:09:54 +00:00
LogPrint ( eLogWarning , " SSU: Address size " , size , " is not supported " ) ;
2014-10-30 19:13:29 +00:00
}
void SSUSession : : FillHeaderAndEncrypt ( uint8_t payloadType , uint8_t * buf , size_t len ,
2016-02-25 23:40:40 +00:00
const i2p : : crypto : : AESKey & aesKey , const uint8_t * iv , const i2p : : crypto : : MACKey & macKey , uint8_t flag )
2014-10-30 19:13:29 +00:00
{
if ( len < sizeof ( SSUHeader ) )
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogError , " SSU: Unexpected packet length " , len ) ;
2014-10-30 19:13:29 +00:00
return ;
}
SSUHeader * header = ( SSUHeader * ) buf ;
memcpy ( header - > iv , iv , 16 ) ;
2016-02-25 23:40:40 +00:00
header - > flag = flag | ( payloadType < < 4 ) ; // MSB is 0
2016-01-10 00:24:52 +00:00
htobe32buf ( header - > time , i2p : : util : : GetSecondsSinceEpoch ( ) ) ;
2014-10-30 19:13:29 +00:00
uint8_t * encrypted = & header - > flag ;
uint16_t encryptedLen = len - ( encrypted - buf ) ;
i2p : : crypto : : CBCEncryption encryption ;
encryption . SetKey ( aesKey ) ;
encryption . SetIV ( iv ) ;
encryption . Encrypt ( encrypted , encryptedLen , encrypted ) ;
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy ( buf + len , iv , 16 ) ;
2014-12-30 14:37:24 +00:00
htobe16buf ( buf + len + 16 , encryptedLen ) ;
2014-10-30 19:13:29 +00:00
i2p : : crypto : : HMACMD5Digest ( encrypted , encryptedLen + 18 , macKey , header - > mac ) ;
}
void SSUSession : : FillHeaderAndEncrypt ( uint8_t payloadType , uint8_t * buf , size_t len )
{
if ( len < sizeof ( SSUHeader ) )
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogError , " SSU: Unexpected packet length " , len ) ;
2014-10-30 19:13:29 +00:00
return ;
}
SSUHeader * header = ( SSUHeader * ) buf ;
2015-11-03 14:15:49 +00:00
RAND_bytes ( header - > iv , 16 ) ; // random iv
2014-10-30 19:13:29 +00:00
m_SessionKeyEncryption . SetIV ( header - > iv ) ;
header - > flag = payloadType < < 4 ; // MSB is 0
2016-01-10 00:24:52 +00:00
htobe32buf ( header - > time , i2p : : util : : GetSecondsSinceEpoch ( ) ) ;
2014-10-30 19:13:29 +00:00
uint8_t * encrypted = & header - > flag ;
uint16_t encryptedLen = len - ( encrypted - buf ) ;
m_SessionKeyEncryption . Encrypt ( encrypted , encryptedLen , encrypted ) ;
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy ( buf + len , header - > iv , 16 ) ;
2014-12-30 14:37:24 +00:00
htobe16buf ( buf + len + 16 , encryptedLen ) ;
2014-10-30 19:13:29 +00:00
i2p : : crypto : : HMACMD5Digest ( encrypted , encryptedLen + 18 , m_MacKey , header - > mac ) ;
}
2015-11-03 14:15:49 +00:00
void SSUSession : : Decrypt ( uint8_t * buf , size_t len , const i2p : : crypto : : AESKey & aesKey )
2014-10-30 19:13:29 +00:00
{
if ( len < sizeof ( SSUHeader ) )
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogError , " SSU: Unexpected packet length " , len ) ;
2014-10-30 19:13:29 +00:00
return ;
2016-01-10 00:24:52 +00:00
}
2014-10-30 19:13:29 +00:00
SSUHeader * header = ( SSUHeader * ) buf ;
uint8_t * encrypted = & header - > flag ;
uint16_t encryptedLen = len - ( encrypted - buf ) ;
i2p : : crypto : : CBCDecryption decryption ;
decryption . SetKey ( aesKey ) ;
decryption . SetIV ( header - > iv ) ;
decryption . Decrypt ( encrypted , encryptedLen , encrypted ) ;
}
void SSUSession : : DecryptSessionKey ( uint8_t * buf , size_t len )
{
if ( len < sizeof ( SSUHeader ) )
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogError , " SSU: Unexpected packet length " , len ) ;
2014-10-30 19:13:29 +00:00
return ;
2016-01-10 00:24:52 +00:00
}
2014-10-30 19:13:29 +00:00
SSUHeader * header = ( SSUHeader * ) buf ;
uint8_t * encrypted = & header - > flag ;
uint16_t encryptedLen = len - ( encrypted - buf ) ;
if ( encryptedLen > 0 )
{
m_SessionKeyDecryption . SetIV ( header - > iv ) ;
m_SessionKeyDecryption . Decrypt ( encrypted , encryptedLen , encrypted ) ;
}
}
2015-11-03 14:15:49 +00:00
bool SSUSession : : Validate ( uint8_t * buf , size_t len , const i2p : : crypto : : MACKey & macKey )
2014-10-30 19:13:29 +00:00
{
if ( len < sizeof ( SSUHeader ) )
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogError , " SSU: Unexpected packet length " , len ) ;
2014-10-30 19:13:29 +00:00
return false ;
2016-01-10 00:24:52 +00:00
}
2014-10-30 19:13:29 +00:00
SSUHeader * header = ( SSUHeader * ) buf ;
uint8_t * encrypted = & header - > flag ;
uint16_t encryptedLen = len - ( encrypted - buf ) ;
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy ( buf + len , header - > iv , 16 ) ;
2014-12-30 14:37:24 +00:00
htobe16buf ( buf + len + 16 , encryptedLen ) ;
2014-10-30 19:13:29 +00:00
uint8_t digest [ 16 ] ;
i2p : : crypto : : HMACMD5Digest ( encrypted , encryptedLen + 18 , macKey , digest ) ;
return ! memcmp ( header - > mac , digest , 16 ) ;
}
void SSUSession : : Connect ( )
{
if ( m_State = = eSessionStateUnknown )
{
// set connect timer
ScheduleConnectTimer ( ) ;
m_DHKeysPair = transports . GetNextDHKeysPair ( ) ;
SendSessionRequest ( ) ;
}
}
2014-11-24 16:18:12 +00:00
void SSUSession : : WaitForConnect ( )
{
2015-11-03 14:15:49 +00:00
if ( ! IsOutgoing ( ) ) // incoming session
2014-11-24 16:18:12 +00:00
ScheduleConnectTimer ( ) ;
else
2015-12-17 08:09:54 +00:00
LogPrint ( eLogError , " SSU: wait for connect for outgoing session " ) ;
2014-11-24 16:18:12 +00:00
}
2014-10-30 19:13:29 +00:00
void SSUSession : : ScheduleConnectTimer ( )
{
2016-08-24 15:21:49 +00:00
m_ConnectTimer . cancel ( ) ;
m_ConnectTimer . expires_from_now ( boost : : posix_time : : seconds ( SSU_CONNECT_TIMEOUT ) ) ;
m_ConnectTimer . async_wait ( std : : bind ( & SSUSession : : HandleConnectTimer ,
2014-11-24 17:26:11 +00:00
shared_from_this ( ) , std : : placeholders : : _1 ) ) ;
2014-10-30 19:13:29 +00:00
}
void SSUSession : : HandleConnectTimer ( const boost : : system : : error_code & ecode )
{
if ( ! ecode )
{
// timeout expired
2016-06-27 13:00:00 +00:00
LogPrint ( eLogWarning , " SSU: session with " , m_RemoteEndpoint , " was not established after " , SSU_CONNECT_TIMEOUT , " seconds " ) ;
2014-10-30 19:13:29 +00:00
Failed ( ) ;
}
}
2015-12-10 03:17:43 +00:00
void SSUSession : : Introduce ( const i2p : : data : : RouterInfo : : Introducer & introducer ,
std : : shared_ptr < const i2p : : data : : RouterInfo > to )
2014-10-30 19:13:29 +00:00
{
if ( m_State = = eSessionStateUnknown )
{
// set connect timer
2016-08-24 15:21:49 +00:00
m_ConnectTimer . expires_from_now ( boost : : posix_time : : seconds ( SSU_CONNECT_TIMEOUT ) ) ;
m_ConnectTimer . async_wait ( std : : bind ( & SSUSession : : HandleConnectTimer ,
2014-11-24 17:26:11 +00:00
shared_from_this ( ) , std : : placeholders : : _1 ) ) ;
2015-12-10 03:17:43 +00:00
}
uint32_t nonce ;
RAND_bytes ( ( uint8_t * ) & nonce , 4 ) ;
m_RelayRequests [ nonce ] = to ;
SendRelayRequest ( introducer , nonce ) ;
2014-10-30 19:13:29 +00:00
}
void SSUSession : : WaitForIntroduction ( )
{
m_State = eSessionStateIntroduced ;
// set connect timer
2016-08-24 15:21:49 +00:00
m_ConnectTimer . expires_from_now ( boost : : posix_time : : seconds ( SSU_CONNECT_TIMEOUT ) ) ;
m_ConnectTimer . async_wait ( std : : bind ( & SSUSession : : HandleConnectTimer ,
2014-11-24 17:26:11 +00:00
shared_from_this ( ) , std : : placeholders : : _1 ) ) ;
2014-10-30 19:13:29 +00:00
}
void SSUSession : : Close ( )
{
2015-02-07 20:25:06 +00:00
m_State = eSessionStateClosed ;
2014-10-30 19:13:29 +00:00
SendSesionDestroyed ( ) ;
2015-01-13 03:53:35 +00:00
transports . PeerDisconnected ( shared_from_this ( ) ) ;
2015-02-07 22:58:29 +00:00
m_Data . Stop ( ) ;
2016-08-24 15:21:49 +00:00
m_ConnectTimer . cancel ( ) ;
2016-12-30 03:06:33 +00:00
if ( m_SentRelayTag )
m_Server . RemoveRelay ( m_SentRelayTag ) ; // relay tag is not valid anymore
2014-10-30 19:13:29 +00:00
}
2015-02-07 01:53:48 +00:00
void SSUSession : : Done ( )
2015-02-06 21:03:47 +00:00
{
2015-02-07 20:25:06 +00:00
GetService ( ) . post ( std : : bind ( & SSUSession : : Failed , shared_from_this ( ) ) ) ;
2015-02-06 21:03:47 +00:00
}
2014-10-30 19:13:29 +00:00
void SSUSession : : Established ( )
{
m_State = eSessionStateEstablished ;
2015-11-03 14:15:49 +00:00
m_DHKeysPair = nullptr ;
m_SignedData = nullptr ;
2015-02-08 13:50:05 +00:00
m_Data . Start ( ) ;
2015-01-13 03:53:35 +00:00
transports . PeerConnected ( shared_from_this ( ) ) ;
2015-11-03 14:15:49 +00:00
if ( m_IsPeerTest )
2014-10-30 19:13:29 +00:00
SendPeerTest ( ) ;
2016-12-31 18:52:26 +00:00
if ( m_SentRelayTag )
m_Server . AddRelay ( m_SentRelayTag , shared_from_this ( ) ) ;
2016-08-24 15:21:49 +00:00
m_LastActivityTimestamp = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2014-10-30 19:13:29 +00:00
}
void SSUSession : : Failed ( )
{
if ( m_State ! = eSessionStateFailed )
{
m_State = eSessionStateFailed ;
2014-11-24 17:26:11 +00:00
m_Server . DeleteSession ( shared_from_this ( ) ) ;
2014-10-30 19:13:29 +00:00
}
}
2015-06-17 14:47:26 +00:00
void SSUSession : : SendI2NPMessages ( const std : : vector < std : : shared_ptr < I2NPMessage > > & msgs )
2015-01-21 02:05:57 +00:00
{
2015-02-07 20:25:06 +00:00
GetService ( ) . post ( std : : bind ( & SSUSession : : PostI2NPMessages , shared_from_this ( ) , msgs ) ) ;
2015-01-21 02:05:57 +00:00
}
2015-06-17 14:47:26 +00:00
void SSUSession : : PostI2NPMessages ( std : : vector < std : : shared_ptr < I2NPMessage > > msgs )
2015-01-21 02:05:57 +00:00
{
2015-02-09 21:33:52 +00:00
if ( m_State = = eSessionStateEstablished )
{
2016-08-08 22:53:37 +00:00
for ( const auto & it : msgs )
2015-02-09 21:33:52 +00:00
if ( it ) m_Data . Send ( it ) ;
}
2015-01-21 02:05:57 +00:00
}
2014-10-30 19:13:29 +00:00
void SSUSession : : ProcessData ( uint8_t * buf , size_t len )
{
m_Data . ProcessMessage ( buf , len ) ;
2015-02-15 19:17:55 +00:00
m_IsDataReceived = true ;
2014-10-30 19:13:29 +00:00
}
2015-02-15 19:17:55 +00:00
void SSUSession : : FlushData ( )
{
if ( m_IsDataReceived )
{
m_Data . FlushReceivedMessage ( ) ;
m_IsDataReceived = false ;
}
}
2014-10-30 19:13:29 +00:00
2015-03-26 18:35:20 +00:00
void SSUSession : : ProcessPeerTest ( const uint8_t * buf , size_t len , const boost : : asio : : ip : : udp : : endpoint & senderEndpoint )
2014-10-30 19:13:29 +00:00
{
2015-03-26 19:05:52 +00:00
uint32_t nonce = bufbe32toh ( buf ) ; // 4 bytes
uint8_t size = buf [ 4 ] ; // 1 byte
2016-07-22 16:50:03 +00:00
const uint8_t * address = buf + 5 ; // big endian, size bytes
2015-03-26 19:05:52 +00:00
uint16_t port = buf16toh ( buf + size + 5 ) ; // big endian, 2 bytes
const uint8_t * introKey = buf + size + 7 ;
2016-07-22 16:50:03 +00:00
if ( port & & ( size ! = 4 ) & & ( size ! = 16 ) )
2014-10-30 19:13:29 +00:00
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogWarning , " SSU: Address of " , size , " bytes not supported " ) ;
2014-10-30 19:13:29 +00:00
return ;
}
2015-02-26 02:56:51 +00:00
switch ( m_Server . GetPeerTestParticipant ( nonce ) )
{
2015-02-25 21:39:48 +00:00
// existing test
2015-02-26 02:56:51 +00:00
case ePeerTestParticipantAlice1 :
{
2016-11-06 01:08:14 +00:00
if ( m_Server . GetPeerTestSession ( nonce ) = = shared_from_this ( ) ) // Alice-Bob
2015-02-26 18:44:18 +00:00
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: peer test from Bob. We are Alice " ) ;
2015-02-26 18:44:18 +00:00
if ( i2p : : context . GetStatus ( ) = = eRouterStatusTesting ) // still not OK
i2p : : context . SetStatus ( eRouterStatusFirewalled ) ;
}
2015-02-25 21:39:48 +00:00
else
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: first peer test from Charlie. We are Alice " ) ;
2016-11-06 01:08:14 +00:00
if ( m_State = = eSessionStateEstablished )
LogPrint ( eLogWarning , " SSU: first peer test from Charlie through established session. We are Alice " ) ;
2015-02-26 18:44:18 +00:00
i2p : : context . SetStatus ( eRouterStatusOK ) ;
2015-02-26 02:56:51 +00:00
m_Server . UpdatePeerTest ( nonce , ePeerTestParticipantAlice2 ) ;
2016-07-22 16:50:03 +00:00
SendPeerTest ( nonce , senderEndpoint . address ( ) , senderEndpoint . port ( ) , introKey , true , false ) ; // to Charlie
2015-02-25 21:39:48 +00:00
}
2015-02-26 02:56:51 +00:00
break ;
}
case ePeerTestParticipantAlice2 :
{
2016-11-06 01:08:14 +00:00
if ( m_Server . GetPeerTestSession ( nonce ) = = shared_from_this ( ) ) // Alice-Bob
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: peer test from Bob. We are Alice " ) ;
2015-02-26 02:56:51 +00:00
else
{
// peer test successive
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: second peer test from Charlie. We are Alice " ) ;
2015-02-26 18:44:18 +00:00
i2p : : context . SetStatus ( eRouterStatusOK ) ;
2015-02-26 02:56:51 +00:00
m_Server . RemovePeerTest ( nonce ) ;
}
break ;
}
case ePeerTestParticipantBob :
2014-10-30 19:13:29 +00:00
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: peer test from Charlie. We are Bob " ) ;
2015-03-26 20:10:52 +00:00
auto session = m_Server . GetPeerTestSession ( nonce ) ; // session with Alice from PeerTest
if ( session & & session - > m_State = = eSessionStateEstablished )
2015-03-26 19:05:52 +00:00
session - > Send ( PAYLOAD_TYPE_PEER_TEST , buf , len ) ; // back to Alice
2015-03-26 18:35:20 +00:00
m_Server . RemovePeerTest ( nonce ) ; // nonce has been used
2015-02-26 02:56:51 +00:00
break ;
2014-10-30 19:13:29 +00:00
}
2015-02-26 02:56:51 +00:00
case ePeerTestParticipantCharlie :
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: peer test from Alice. We are Charlie " ) ;
2016-07-22 16:50:03 +00:00
SendPeerTest ( nonce , senderEndpoint . address ( ) , senderEndpoint . port ( ) , introKey ) ; // to Alice with her actual address
2015-03-26 18:35:20 +00:00
m_Server . RemovePeerTest ( nonce ) ; // nonce has been used
2015-02-26 02:56:51 +00:00
break ;
2014-10-30 19:13:29 +00:00
}
2015-02-26 02:56:51 +00:00
// test not found
case ePeerTestParticipantUnknown :
2014-10-30 19:13:29 +00:00
{
2015-02-26 02:56:51 +00:00
if ( m_State = = eSessionStateEstablished )
2014-10-30 19:13:29 +00:00
{
2015-02-26 02:56:51 +00:00
// new test
if ( port )
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: peer test from Bob. We are Charlie " ) ;
2015-02-26 02:56:51 +00:00
m_Server . NewPeerTest ( nonce , ePeerTestParticipantCharlie ) ;
2015-03-26 19:05:52 +00:00
Send ( PAYLOAD_TYPE_PEER_TEST , buf , len ) ; // back to Bob
2016-07-22 16:50:03 +00:00
boost : : asio : : ip : : address addr ; // Alice's address
if ( size = = 4 ) // v4
{
boost : : asio : : ip : : address_v4 : : bytes_type bytes ;
memcpy ( bytes . data ( ) , address , 4 ) ;
addr = boost : : asio : : ip : : address_v4 ( bytes ) ;
}
else // v6
{
boost : : asio : : ip : : address_v6 : : bytes_type bytes ;
2016-09-01 02:47:32 +00:00
memcpy ( bytes . data ( ) , address , 16 ) ;
2016-07-22 16:50:03 +00:00
addr = boost : : asio : : ip : : address_v6 ( bytes ) ;
}
SendPeerTest ( nonce , addr , be16toh ( port ) , introKey ) ; // to Alice with her address received from Bob
2015-02-26 02:56:51 +00:00
}
else
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: peer test from Alice. We are Bob " ) ;
2016-07-22 17:08:41 +00:00
auto session = senderEndpoint . address ( ) . is_v4 ( ) ? m_Server . GetRandomEstablishedV4Session ( shared_from_this ( ) ) : m_Server . GetRandomEstablishedV6Session ( shared_from_this ( ) ) ; // Charlie
2015-02-26 02:56:51 +00:00
if ( session )
{
2015-03-26 20:10:52 +00:00
m_Server . NewPeerTest ( nonce , ePeerTestParticipantBob , shared_from_this ( ) ) ;
2016-07-22 16:50:03 +00:00
session - > SendPeerTest ( nonce , senderEndpoint . address ( ) , senderEndpoint . port ( ) , introKey , false ) ; // to Charlie with Alice's actual address
2015-02-26 02:56:51 +00:00
}
}
2014-10-30 19:13:29 +00:00
}
else
2015-12-17 08:09:54 +00:00
LogPrint ( eLogError , " SSU: unexpected peer test " ) ;
2014-10-30 19:13:29 +00:00
}
}
}
2016-07-22 16:50:03 +00:00
void SSUSession : : SendPeerTest ( uint32_t nonce , const boost : : asio : : ip : : address & address , uint16_t port ,
2015-02-25 21:39:48 +00:00
const uint8_t * introKey , bool toAddress , bool sendAddress )
// toAddress is true for Alice<->Chalie communications only
// sendAddress is false if message comes from Alice
2014-10-30 19:13:29 +00:00
{
uint8_t buf [ 80 + 18 ] ;
uint8_t iv [ 16 ] ;
uint8_t * payload = buf + sizeof ( SSUHeader ) ;
2014-12-30 14:37:24 +00:00
htobe32buf ( payload , nonce ) ;
2014-10-30 19:13:29 +00:00
payload + = 4 ; // nonce
2015-02-25 21:39:48 +00:00
// address and port
2016-07-22 16:50:03 +00:00
if ( sendAddress )
{
if ( address . is_v4 ( ) )
{
* payload = 4 ;
memcpy ( payload + 1 , address . to_v4 ( ) . to_bytes ( ) . data ( ) , 4 ) ; // our IP V4
}
else if ( address . is_v6 ( ) )
{
2016-09-01 02:47:32 +00:00
* payload = 16 ;
2016-07-22 16:50:03 +00:00
memcpy ( payload + 1 , address . to_v6 ( ) . to_bytes ( ) . data ( ) , 16 ) ; // our IP V6
}
else
* payload = 0 ;
payload + = ( payload [ 0 ] + 1 ) ;
2014-10-30 19:13:29 +00:00
}
else
{
* payload = 0 ;
payload + + ; //size
}
2014-12-30 14:37:24 +00:00
htobe16buf ( payload , port ) ;
2014-10-30 19:13:29 +00:00
payload + = 2 ; // port
2015-02-25 21:39:48 +00:00
// intro key
if ( toAddress )
{
// send our intro key to address instead it's own
auto addr = i2p : : context . GetRouterInfo ( ) . GetSSUAddress ( ) ;
if ( addr )
2017-01-02 21:36:59 +00:00
memcpy ( payload , addr - > ssu - > key , 32 ) ; // intro key
2015-02-25 21:39:48 +00:00
else
2016-01-28 02:54:42 +00:00
LogPrint ( eLogInfo , " SSU is not supported. Can't send peer test " ) ;
2015-02-25 21:39:48 +00:00
}
else
memcpy ( payload , introKey , 32 ) ; // intro key
2014-10-30 19:13:29 +00:00
2015-02-25 21:39:48 +00:00
// send
2015-11-03 14:15:49 +00:00
RAND_bytes ( iv , 16 ) ; // random iv
2014-10-30 19:13:29 +00:00
if ( toAddress )
{
// encrypt message with specified intro key
FillHeaderAndEncrypt ( PAYLOAD_TYPE_PEER_TEST , buf , 80 , introKey , iv , introKey ) ;
2016-07-22 16:50:03 +00:00
boost : : asio : : ip : : udp : : endpoint e ( address , port ) ;
2014-10-30 19:13:29 +00:00
m_Server . Send ( buf , 80 , e ) ;
}
else
{
// encrypt message with session key
FillHeaderAndEncrypt ( PAYLOAD_TYPE_PEER_TEST , buf , 80 ) ;
Send ( buf , 80 ) ;
}
}
void SSUSession : : SendPeerTest ( )
{
2015-02-25 20:26:06 +00:00
// we are Alice
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: sending peer test " ) ;
2016-12-11 14:53:43 +00:00
auto address = i2p : : context . GetRouterInfo ( ) . GetSSUAddress ( i2p : : context . SupportsV4 ( ) ) ;
2014-10-30 19:13:29 +00:00
if ( ! address )
{
2016-01-28 02:54:42 +00:00
LogPrint ( eLogInfo , " SSU is not supported. Can't send peer test " ) ;
2014-10-30 19:13:29 +00:00
return ;
}
2015-11-03 14:15:49 +00:00
uint32_t nonce ;
RAND_bytes ( ( uint8_t * ) & nonce , 4 ) ;
2014-10-30 19:13:29 +00:00
if ( ! nonce ) nonce = 1 ;
2015-11-03 14:15:49 +00:00
m_IsPeerTest = false ;
2016-11-09 17:13:42 +00:00
m_Server . NewPeerTest ( nonce , ePeerTestParticipantAlice1 , shared_from_this ( ) ) ;
2017-01-02 21:36:59 +00:00
SendPeerTest ( nonce , boost : : asio : : ip : : address ( ) , 0 , address - > ssu - > key , false , false ) ; // address and port always zero for Alice
2014-10-30 19:13:29 +00:00
}
void SSUSession : : SendKeepAlive ( )
{
if ( m_State = = eSessionStateEstablished )
{
uint8_t buf [ 48 + 18 ] ;
uint8_t * payload = buf + sizeof ( SSUHeader ) ;
* payload = 0 ; // flags
payload + + ;
* payload = 0 ; // num fragments
// encrypt message with session key
FillHeaderAndEncrypt ( PAYLOAD_TYPE_DATA , buf , 48 ) ;
Send ( buf , 48 ) ;
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: keep-alive sent " ) ;
2016-08-24 15:21:49 +00:00
m_LastActivityTimestamp = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2014-10-30 19:13:29 +00:00
}
}
void SSUSession : : SendSesionDestroyed ( )
{
if ( m_IsSessionKey )
{
uint8_t buf [ 48 + 18 ] ;
// encrypt message with session key
FillHeaderAndEncrypt ( PAYLOAD_TYPE_SESSION_DESTROYED , buf , 48 ) ;
2015-02-27 18:07:32 +00:00
try
{
Send ( buf , 48 ) ;
}
catch ( std : : exception & ex )
{
2016-01-28 02:54:42 +00:00
LogPrint ( eLogWarning , " SSU: exception while sending session destoroyed: " , ex . what ( ) ) ;
2015-02-27 18:07:32 +00:00
}
2015-12-17 08:09:54 +00:00
LogPrint ( eLogDebug , " SSU: session destroyed sent " ) ;
2014-10-30 19:13:29 +00:00
}
}
void SSUSession : : Send ( uint8_t type , const uint8_t * payload , size_t len )
{
uint8_t buf [ SSU_MTU_V4 + 18 ] ;
size_t msgSize = len + sizeof ( SSUHeader ) ;
2015-03-25 13:04:19 +00:00
size_t paddingSize = msgSize & 0x0F ; // %16
2015-03-18 17:30:38 +00:00
if ( paddingSize > 0 ) msgSize + = ( 16 - paddingSize ) ;
2014-10-30 19:13:29 +00:00
if ( msgSize > SSU_MTU_V4 )
{
2015-12-17 08:09:54 +00:00
LogPrint ( eLogWarning , " SSU: payload size " , msgSize , " exceeds MTU " ) ;
2014-10-30 19:13:29 +00:00
return ;
}
memcpy ( buf + sizeof ( SSUHeader ) , payload , len ) ;
// encrypt message with session key
FillHeaderAndEncrypt ( type , buf , msgSize ) ;
Send ( buf , msgSize ) ;
}
void SSUSession : : Send ( const uint8_t * buf , size_t size )
{
m_NumSentBytes + = size ;
2015-03-16 23:33:59 +00:00
i2p : : transport : : transports . UpdateSentBytes ( size ) ;
2014-10-30 19:13:29 +00:00
m_Server . Send ( buf , size , m_RemoteEndpoint ) ;
}
}
}