2020-05-22 13:18:41 +00:00
/*
2024-01-30 20:41:57 +00:00
* Copyright ( c ) 2013 - 2024 , The PurpleI2P Project
2020-05-22 13:18:41 +00:00
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
2020-01-15 20:13:43 +00:00
# include <string.h>
# include <openssl/sha.h>
# include "Log.h"
2020-12-27 16:18:53 +00:00
# include "util.h"
2020-01-15 20:13:43 +00:00
# include "Crypto.h"
# include "Elligator.h"
# include "Tag.h"
# include "I2PEndian.h"
2020-01-23 19:26:40 +00:00
# include "Timestamp.h"
2020-03-08 22:13:41 +00:00
# include "Tunnel.h"
# include "TunnelPool.h"
2020-06-30 17:00:41 +00:00
# include "Transports.h"
2020-01-15 20:13:43 +00:00
# include "ECIESX25519AEADRatchetSession.h"
namespace i2p
{
namespace garlic
{
2020-03-01 10:25:50 +00:00
void RatchetTagSet : : DHInitialize ( const uint8_t * rootKey , const uint8_t * k )
{
// DH_INITIALIZE(rootKey, k)
uint8_t keydata [ 64 ] ;
i2p : : crypto : : HKDF ( rootKey , k , 32 , " KDFDHRatchetStep " , keydata ) ; // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64)
memcpy ( m_NextRootKey , keydata , 32 ) ; // nextRootKey = keydata[0:31]
2021-08-01 22:42:13 +00:00
i2p : : crypto : : HKDF ( keydata + 32 , nullptr , 0 , " TagAndKeyGenKeys " , m_SessionTagKeyData ) ;
2020-03-01 10:25:50 +00:00
// [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64)
2021-08-01 22:42:13 +00:00
memcpy ( m_SymmKeyCK , ( const uint8_t * ) m_SessionTagKeyData + 32 , 32 ) ;
2020-02-09 02:51:02 +00:00
m_NextSymmKeyIndex = 0 ;
2020-03-01 10:25:50 +00:00
}
2020-01-20 20:17:38 +00:00
2020-03-01 10:25:50 +00:00
void RatchetTagSet : : NextSessionTagRatchet ( )
{
2021-08-01 22:42:13 +00:00
i2p : : crypto : : HKDF ( m_SessionTagKeyData , nullptr , 0 , " STInitialization " , m_SessionTagKeyData ) ; // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64)
memcpy ( m_SessTagConstant , ( const uint8_t * ) m_SessionTagKeyData + 32 , 32 ) ; // SESSTAG_CONSTANT = keydata[32:63]
2020-02-05 20:48:51 +00:00
m_NextIndex = 0 ;
2020-03-01 10:25:50 +00:00
}
2020-01-20 20:17:38 +00:00
2020-03-01 10:25:50 +00:00
uint64_t RatchetTagSet : : GetNextSessionTag ( )
{
m_NextIndex + + ;
if ( m_NextIndex > = 65535 )
2020-04-26 23:27:31 +00:00
{
LogPrint ( eLogError , " Garlic: Tagset " , GetTagSetID ( ) , " is empty " ) ;
return 0 ;
2020-03-01 10:25:50 +00:00
}
2021-08-01 22:42:13 +00:00
i2p : : crypto : : HKDF ( m_SessionTagKeyData , m_SessTagConstant , 32 , " SessionTagKeyGen " , m_SessionTagKeyData ) ; // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
return m_SessionTagKeyData . GetLL ( ) [ 4 ] ; // tag = keydata[32:39]
2020-03-01 10:25:50 +00:00
}
2020-01-20 20:17:38 +00:00
2020-03-07 23:46:40 +00:00
void RatchetTagSet : : GetSymmKey ( int index , uint8_t * key )
2020-02-09 02:51:02 +00:00
{
2020-04-17 01:30:18 +00:00
if ( index > = m_NextSymmKeyIndex )
2020-03-01 10:25:50 +00:00
{
2020-03-07 23:46:40 +00:00
auto num = index + 1 - m_NextSymmKeyIndex ;
2020-04-17 01:30:18 +00:00
if ( ! m_NextSymmKeyIndex )
{
i2p : : crypto : : HKDF ( m_SymmKeyCK , nullptr , 0 , " SymmetricRatchet " , m_CurrentSymmKeyCK ) ; // keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64)
m_NextSymmKeyIndex = 1 ;
num - - ;
2020-03-01 10:25:50 +00:00
}
2020-03-07 23:46:40 +00:00
for ( int i = 0 ; i < num ; i + + )
2020-03-01 10:25:50 +00:00
{
2020-03-07 23:46:40 +00:00
i2p : : crypto : : HKDF ( m_CurrentSymmKeyCK , nullptr , 0 , " SymmetricRatchet " , m_CurrentSymmKeyCK ) ;
2020-04-17 01:30:18 +00:00
if ( i < num - 1 )
m_ItermediateSymmKeys . emplace ( m_NextSymmKeyIndex + i , m_CurrentSymmKeyCK + 32 ) ;
2020-03-01 10:25:50 +00:00
}
2020-03-07 23:46:40 +00:00
m_NextSymmKeyIndex + = num ;
memcpy ( key , m_CurrentSymmKeyCK + 32 , 32 ) ;
2020-02-09 02:51:02 +00:00
}
else
2020-04-17 01:30:18 +00:00
{
auto it = m_ItermediateSymmKeys . find ( index ) ;
if ( it ! = m_ItermediateSymmKeys . end ( ) )
2020-03-01 10:25:50 +00:00
{
2020-04-17 01:30:18 +00:00
memcpy ( key , it - > second , 32 ) ;
m_ItermediateSymmKeys . erase ( it ) ;
2020-03-01 10:25:50 +00:00
}
2020-04-17 01:30:18 +00:00
else
LogPrint ( eLogError , " Garlic: Missing symmetric key for index " , index ) ;
2020-03-01 10:25:50 +00:00
}
}
2020-05-01 01:27:35 +00:00
2020-09-08 11:46:55 +00:00
void RatchetTagSet : : DeleteSymmKey ( int index )
2021-11-27 20:30:35 +00:00
{
2020-09-08 11:46:55 +00:00
m_ItermediateSymmKeys . erase ( index ) ;
}
2021-11-27 20:30:35 +00:00
2021-01-05 20:56:48 +00:00
void ReceiveRatchetTagSet : : Expire ( )
2020-05-01 01:27:35 +00:00
{
if ( ! m_ExpirationTimestamp )
m_ExpirationTimestamp = i2p : : util : : GetSecondsSinceEpoch ( ) + ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT ;
2020-03-01 10:25:50 +00:00
}
2021-11-27 20:30:35 +00:00
bool ReceiveRatchetTagSet : : IsExpired ( uint64_t ts ) const
{
return m_ExpirationTimestamp & & ts > m_ExpirationTimestamp ;
}
bool ReceiveRatchetTagSet : : IsIndexExpired ( int index ) const
{
return index < m_TrimBehindIndex ;
2021-01-05 01:15:48 +00:00
}
2021-01-04 23:20:16 +00:00
bool ReceiveRatchetTagSet : : HandleNextMessage ( uint8_t * buf , size_t len , int index )
2020-09-15 23:39:18 +00:00
{
auto session = GetSession ( ) ;
if ( ! session ) return false ;
return session - > HandleNextMessage ( buf , len , shared_from_this ( ) , index ) ;
2021-11-27 20:30:35 +00:00
}
2020-09-15 23:39:18 +00:00
2024-02-17 02:30:40 +00:00
bool ReceiveRatchetTagSet : : IsSessionTerminated ( ) const
{
return ! m_Session | | m_Session - > IsTerminated ( ) ;
}
2021-05-22 22:41:25 +00:00
SymmetricKeyTagSet : : SymmetricKeyTagSet ( GarlicDestination * destination , const uint8_t * key ) :
2021-11-27 20:30:35 +00:00
ReceiveRatchetTagSet ( nullptr ) , m_Destination ( destination )
{
memcpy ( m_Key , key , 32 ) ;
Expire ( ) ;
2020-09-15 23:39:18 +00:00
}
2021-11-27 20:30:35 +00:00
2021-05-22 22:41:25 +00:00
bool SymmetricKeyTagSet : : HandleNextMessage ( uint8_t * buf , size_t len , int index )
2020-09-15 23:39:18 +00:00
{
if ( len < 24 ) return false ;
uint8_t nonce [ 12 ] ;
memset ( nonce , 0 , 12 ) ; // n = 0
2021-11-27 20:30:35 +00:00
size_t offset = 8 ; // first 8 bytes is reply tag used as AD
2020-09-15 23:39:18 +00:00
len - = 16 ; // poly1305
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( buf + offset , len - offset , buf , 8 , m_Key , nonce , buf + offset , len - offset , false ) ) // decrypt
{
2021-05-22 22:41:25 +00:00
LogPrint ( eLogWarning , " Garlic: Symmetric key tagset AEAD decryption failed " ) ;
2020-09-15 23:39:18 +00:00
return false ;
}
// we assume 1 I2NP block with delivery type local
2021-11-27 20:30:35 +00:00
if ( offset + 3 > len )
{
2021-05-22 22:41:25 +00:00
LogPrint ( eLogWarning , " Garlic: Symmetric key tagset is too short " , len ) ;
2020-09-15 23:39:18 +00:00
return false ;
2021-11-27 20:30:35 +00:00
}
2020-09-15 23:39:18 +00:00
if ( buf [ offset ] ! = eECIESx25519BlkGalicClove )
{
2021-05-22 22:41:25 +00:00
LogPrint ( eLogWarning , " Garlic: Symmetric key tagset unexpected block " , ( int ) buf [ offset ] ) ;
2020-09-15 23:39:18 +00:00
return false ;
2021-11-27 20:30:35 +00:00
}
2020-09-15 23:39:18 +00:00
offset + + ;
auto size = bufbe16toh ( buf + offset ) ;
offset + = 2 ;
2021-11-27 20:30:35 +00:00
if ( offset + size > len )
2020-09-15 23:39:18 +00:00
{
2021-05-22 22:41:25 +00:00
LogPrint ( eLogWarning , " Garlic: Symmetric key tagset block is too long " , size ) ;
2020-09-15 23:39:18 +00:00
return false ;
2021-11-27 20:30:35 +00:00
}
2020-09-15 23:39:18 +00:00
if ( m_Destination )
2021-11-27 20:30:35 +00:00
m_Destination - > HandleECIESx25519GarlicClove ( buf + offset , size ) ;
2020-09-15 23:39:18 +00:00
return true ;
2021-11-27 20:30:35 +00:00
}
2021-05-09 11:33:26 +00:00
ECIESX25519AEADRatchetSession : : ECIESX25519AEADRatchetSession ( GarlicDestination * owner , bool attachLeaseSetNS ) :
GarlicRoutingSession ( owner , true )
2020-03-01 10:25:50 +00:00
{
2021-11-27 20:30:35 +00:00
if ( ! attachLeaseSetNS ) SetLeaseSetUpdateStatus ( eLeaseSetUpToDate ) ;
2020-06-14 01:24:16 +00:00
RAND_bytes ( m_PaddingSizes , 32 ) ; m_NextPaddingSize = 0 ;
2020-03-01 10:25:50 +00:00
}
ECIESX25519AEADRatchetSession : : ~ ECIESX25519AEADRatchetSession ( )
{
}
2020-01-15 20:13:43 +00:00
2020-02-05 20:48:51 +00:00
void ECIESX25519AEADRatchetSession : : CreateNonce ( uint64_t seqn , uint8_t * nonce )
{
2020-03-01 10:25:50 +00:00
memset ( nonce , 0 , 4 ) ;
htole64buf ( nonce + 4 , seqn ) ;
}
bool ECIESX25519AEADRatchetSession : : GenerateEphemeralKeysAndEncode ( uint8_t * buf )
{
2020-06-30 17:00:41 +00:00
bool ineligible = false ;
while ( ! ineligible )
2021-11-27 20:30:35 +00:00
{
2020-06-30 17:00:41 +00:00
m_EphemeralKeys = i2p : : transport : : transports . GetNextX25519KeysPair ( ) ;
ineligible = m_EphemeralKeys - > IsElligatorIneligible ( ) ;
if ( ! ineligible ) // we haven't tried it yet
2021-11-27 20:30:35 +00:00
{
2020-06-30 17:00:41 +00:00
if ( i2p : : crypto : : GetElligator ( ) - > Encode ( m_EphemeralKeys - > GetPublicKey ( ) , buf ) )
return true ; // success
// otherwise return back
m_EphemeralKeys - > SetElligatorIneligible ( ) ;
i2p : : transport : : transports . ReuseX25519KeysPair ( m_EphemeralKeys ) ;
}
else
i2p : : transport : : transports . ReuseX25519KeysPair ( m_EphemeralKeys ) ;
2021-11-27 20:30:35 +00:00
}
2020-06-30 17:00:41 +00:00
// we still didn't find elligator eligible pair
2021-05-10 22:55:39 +00:00
for ( int i = 0 ; i < 25 ; i + + )
2020-03-01 10:25:50 +00:00
{
2020-06-30 17:00:41 +00:00
// create new
m_EphemeralKeys = std : : make_shared < i2p : : crypto : : X25519Keys > ( ) ;
m_EphemeralKeys - > GenerateKeys ( ) ;
if ( i2p : : crypto : : GetElligator ( ) - > Encode ( m_EphemeralKeys - > GetPublicKey ( ) , buf ) )
2020-03-01 10:25:50 +00:00
return true ; // success
2020-06-30 17:00:41 +00:00
else
{
// let NTCP2 use it
m_EphemeralKeys - > SetElligatorIneligible ( ) ;
i2p : : transport : : transports . ReuseX25519KeysPair ( m_EphemeralKeys ) ;
2021-11-27 20:30:35 +00:00
}
2020-03-01 10:25:50 +00:00
}
2020-06-30 17:00:41 +00:00
LogPrint ( eLogError , " Garlic: Can't generate elligator eligible x25519 keys " ) ;
2020-03-01 10:25:50 +00:00
return false ;
2020-02-05 20:48:51 +00:00
}
2021-01-05 01:15:48 +00:00
void ECIESX25519AEADRatchetSession : : InitNewSessionTagset ( std : : shared_ptr < RatchetTagSet > tagsetNsr ) const
2020-03-01 10:25:50 +00:00
{
uint8_t tagsetKey [ 32 ] ;
i2p : : crypto : : HKDF ( m_CK , nullptr , 0 , " SessionReplyTags " , tagsetKey , 32 ) ; // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32)
// Session Tag Ratchet
tagsetNsr - > DHInitialize ( m_CK , tagsetKey ) ; // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey)
tagsetNsr - > NextSessionTagRatchet ( ) ;
}
bool ECIESX25519AEADRatchetSession : : HandleNewIncomingSession ( const uint8_t * buf , size_t len )
{
if ( ! GetOwner ( ) ) return false ;
// we are Bob
// KDF1
2021-03-03 20:30:13 +00:00
i2p : : crypto : : InitNoiseIKState ( GetNoiseState ( ) , GetOwner ( ) - > GetEncryptionPublicKey ( i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ) ) ; // bpk
2021-11-27 20:30:35 +00:00
2020-01-30 02:57:10 +00:00
if ( ! i2p : : crypto : : GetElligator ( ) - > Decode ( buf , m_Aepk ) )
2020-03-01 10:25:50 +00:00
{
2020-01-15 20:13:43 +00:00
LogPrint ( eLogError , " Garlic: Can't decode elligator " ) ;
2020-03-01 10:25:50 +00:00
return false ;
2020-01-15 20:13:43 +00:00
}
2020-03-01 10:25:50 +00:00
buf + = 32 ; len - = 32 ;
MixHash ( m_Aepk , 32 ) ; // h = SHA256(h || aepk)
uint8_t sharedSecret [ 32 ] ;
2021-08-31 22:51:40 +00:00
if ( ! GetOwner ( ) - > Decrypt ( m_Aepk , sharedSecret , i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ) ) // x25519(bsk, aepk)
2021-01-01 20:03:11 +00:00
{
LogPrint ( eLogWarning , " Garlic: Incorrect Alice ephemeral key " ) ;
return false ;
2021-11-27 20:30:35 +00:00
}
2020-11-05 20:27:37 +00:00
MixKey ( sharedSecret ) ;
2020-03-01 10:25:50 +00:00
// decrypt flags/static
2020-01-15 20:13:43 +00:00
uint8_t nonce [ 12 ] , fs [ 32 ] ;
2020-02-05 20:48:51 +00:00
CreateNonce ( 0 , nonce ) ;
2020-01-17 00:33:00 +00:00
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( buf , 32 , m_H , 32 , m_CK + 32 , nonce , fs , 32 , false ) ) // decrypt
2020-01-15 20:13:43 +00:00
{
LogPrint ( eLogWarning , " Garlic: Flags/static section AEAD verification failed " ) ;
return false ;
}
MixHash ( buf , 48 ) ; // h = SHA256(h || ciphertext)
buf + = 48 ; len - = 48 ; // 32 data + 16 poly
// KDF2 for payload
2020-03-01 10:25:50 +00:00
bool isStatic = ! i2p : : data : : Tag < 32 > ( fs ) . IsZero ( ) ;
2020-01-15 20:13:43 +00:00
if ( isStatic )
{
// static key, fs is apk
2020-03-01 10:25:50 +00:00
memcpy ( m_RemoteStaticKey , fs , 32 ) ;
2021-08-31 22:51:40 +00:00
if ( ! GetOwner ( ) - > Decrypt ( fs , sharedSecret , i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ) ) // x25519(bsk, apk)
2021-01-01 20:03:11 +00:00
{
LogPrint ( eLogWarning , " Garlic: Incorrect Alice static key " ) ;
return false ;
2021-11-27 20:30:35 +00:00
}
2020-11-05 20:27:37 +00:00
MixKey ( sharedSecret ) ;
2020-01-15 20:13:43 +00:00
}
else // all zeros flags
2020-02-05 20:48:51 +00:00
CreateNonce ( 1 , nonce ) ;
2020-03-01 10:25:50 +00:00
2020-05-12 22:30:04 +00:00
// decrypt payload
std : : vector < uint8_t > payload ( len - 16 ) ; // we must save original ciphertext
2020-01-17 00:33:00 +00:00
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( buf , len - 16 , m_H , 32 , m_CK + 32 , nonce , payload . data ( ) , len - 16 , false ) ) // decrypt
2020-01-15 20:13:43 +00:00
{
LogPrint ( eLogWarning , " Garlic: Payload section AEAD verification failed " ) ;
return false ;
}
2021-11-27 20:30:35 +00:00
2020-03-01 10:25:50 +00:00
m_State = eSessionStateNewSessionReceived ;
2021-11-27 20:30:35 +00:00
if ( isStatic )
{
2020-11-05 20:27:37 +00:00
MixHash ( buf , len ) ; // h = SHA256(h || ciphertext)
GetOwner ( ) - > AddECIESx25519Session ( m_RemoteStaticKey , shared_from_this ( ) ) ;
2021-11-27 20:30:35 +00:00
}
2020-03-01 10:25:50 +00:00
HandlePayload ( payload . data ( ) , len - 16 , nullptr , 0 ) ;
2020-01-15 20:13:43 +00:00
2020-03-01 10:25:50 +00:00
return true ;
}
2020-01-15 20:13:43 +00:00
2021-01-04 23:20:16 +00:00
void ECIESX25519AEADRatchetSession : : HandlePayload ( const uint8_t * buf , size_t len , const std : : shared_ptr < ReceiveRatchetTagSet > & receiveTagset , int index )
2020-03-01 10:25:50 +00:00
{
size_t offset = 0 ;
2020-01-15 20:13:43 +00:00
while ( offset < len )
{
uint8_t blk = buf [ offset ] ;
offset + + ;
auto size = bufbe16toh ( buf + offset ) ;
offset + = 2 ;
LogPrint ( eLogDebug , " Garlic: Block type " , ( int ) blk , " of size " , size ) ;
if ( size > len )
{
LogPrint ( eLogError , " Garlic: Unexpected block length " , size ) ;
break ;
}
switch ( blk )
{
case eECIESx25519BlkGalicClove :
2020-05-05 17:01:23 +00:00
if ( GetOwner ( ) )
GetOwner ( ) - > HandleECIESx25519GarlicClove ( buf + offset , size ) ;
2020-01-15 20:13:43 +00:00
break ;
2020-04-25 01:36:08 +00:00
case eECIESx25519BlkNextKey :
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " Garlic: Next key " ) ;
2020-12-21 00:52:06 +00:00
if ( receiveTagset )
HandleNextKey ( buf + offset , size , receiveTagset ) ;
else
LogPrint ( eLogError , " Garlic: Unexpected next key block " ) ;
2020-04-25 01:36:08 +00:00
break ;
2020-04-19 19:00:51 +00:00
case eECIESx25519BlkAck :
2020-03-01 10:25:50 +00:00
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " Garlic: Ack " ) ;
2020-04-19 19:00:51 +00:00
int numAcks = size > > 2 ; // /4
2020-03-01 10:25:50 +00:00
auto offset1 = offset ;
2020-04-19 19:00:51 +00:00
for ( auto i = 0 ; i < numAcks ; i + + )
2020-03-01 10:25:50 +00:00
{
2020-04-19 19:00:51 +00:00
offset1 + = 2 ; // tagsetid
MessageConfirmed ( bufbe16toh ( buf + offset1 ) ) ; offset1 + = 2 ; // N
}
break ;
2020-03-01 10:25:50 +00:00
}
2020-03-26 23:03:38 +00:00
case eECIESx25519BlkAckRequest :
2020-03-01 10:25:50 +00:00
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " Garlic: Ack request " ) ;
2023-02-20 19:55:59 +00:00
if ( receiveTagset )
m_AckRequests . push_back ( { receiveTagset - > GetTagSetID ( ) , index } ) ;
2020-03-01 10:25:50 +00:00
break ;
}
2020-05-05 17:01:23 +00:00
case eECIESx25519BlkTermination :
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " Garlic: Termination " ) ;
2020-05-05 17:01:23 +00:00
if ( GetOwner ( ) )
GetOwner ( ) - > RemoveECIESx25519Session ( m_RemoteStaticKey ) ;
if ( receiveTagset ) receiveTagset - > Expire ( ) ;
2020-03-01 10:25:50 +00:00
break ;
2020-05-05 17:01:23 +00:00
case eECIESx25519BlkDateTime :
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " Garlic: Datetime " ) ;
2020-03-01 10:25:50 +00:00
break ;
2020-05-05 17:01:23 +00:00
case eECIESx25519BlkOptions :
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " Garlic: Options " ) ;
2020-05-05 17:01:23 +00:00
break ;
case eECIESx25519BlkPadding :
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " Garlic: Padding " ) ;
2020-03-01 10:25:50 +00:00
break ;
2020-01-15 20:13:43 +00:00
default :
LogPrint ( eLogWarning , " Garlic: Unknown block type " , ( int ) blk ) ;
}
offset + = size ;
}
2020-03-01 10:25:50 +00:00
}
2020-01-16 19:59:19 +00:00
2021-01-04 23:20:16 +00:00
void ECIESX25519AEADRatchetSession : : HandleNextKey ( const uint8_t * buf , size_t len , const std : : shared_ptr < ReceiveRatchetTagSet > & receiveTagset )
2020-04-25 01:36:08 +00:00
{
uint8_t flag = buf [ 0 ] ; buf + + ; // flag
if ( flag & ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG )
{
2020-04-27 13:35:02 +00:00
if ( ! m_SendForwardKey | | ! m_NextSendRatchet ) return ;
2020-04-26 23:27:31 +00:00
uint16_t keyID = bufbe16toh ( buf ) ; buf + = 2 ; // keyID
if ( ( ( ! m_NextSendRatchet - > newKey | | ! m_NextSendRatchet - > keyID ) & & keyID = = m_NextSendRatchet - > keyID ) | |
2020-03-01 10:25:50 +00:00
( m_NextSendRatchet - > newKey & & keyID = = m_NextSendRatchet - > keyID - 1 ) )
2020-04-26 23:27:31 +00:00
{
2020-04-27 13:35:02 +00:00
if ( flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG )
2020-04-26 23:27:31 +00:00
memcpy ( m_NextSendRatchet - > remote , buf , 32 ) ;
uint8_t sharedSecret [ 32 ] , tagsetKey [ 32 ] ;
2020-06-30 17:00:41 +00:00
m_NextSendRatchet - > key - > Agree ( m_NextSendRatchet - > remote , sharedSecret ) ;
2020-04-26 23:27:31 +00:00
i2p : : crypto : : HKDF ( sharedSecret , nullptr , 0 , " XDHRatchetTagSet " , tagsetKey , 32 ) ; // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32)
2021-01-04 23:20:16 +00:00
auto newTagset = std : : make_shared < RatchetTagSet > ( ) ;
2020-04-26 23:27:31 +00:00
newTagset - > SetTagSetID ( 1 + m_NextSendRatchet - > keyID + keyID ) ;
2020-03-01 10:25:50 +00:00
newTagset - > DHInitialize ( m_SendTagset - > GetNextRootKey ( ) , tagsetKey ) ;
2020-04-26 23:27:31 +00:00
newTagset - > NextSessionTagRatchet ( ) ;
2020-03-01 10:25:50 +00:00
m_SendTagset = newTagset ;
2020-04-26 23:27:31 +00:00
m_SendForwardKey = false ;
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " Garlic: Next send tagset " , newTagset - > GetTagSetID ( ) , " created " ) ;
2020-04-26 23:27:31 +00:00
}
else
LogPrint ( eLogDebug , " Garlic: Unexpected next key " , keyID ) ;
2020-03-01 10:25:50 +00:00
}
2020-04-25 01:36:08 +00:00
else
{
uint16_t keyID = bufbe16toh ( buf ) ; buf + = 2 ; // keyID
2020-04-26 01:09:03 +00:00
bool newKey = flag & ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG ;
m_SendReverseKey = true ;
if ( ! m_NextReceiveRatchet )
m_NextReceiveRatchet . reset ( new DHRatchet ( ) ) ;
else
2020-03-01 10:25:50 +00:00
{
if ( keyID = = m_NextReceiveRatchet - > keyID & & newKey = = m_NextReceiveRatchet - > newKey )
2020-04-25 18:45:53 +00:00
{
2020-04-26 01:09:03 +00:00
LogPrint ( eLogDebug , " Garlic: Duplicate " , newKey ? " new " : " old " , " key " , keyID , " received " ) ;
return ;
2020-03-01 10:25:50 +00:00
}
2020-04-26 01:09:03 +00:00
m_NextReceiveRatchet - > keyID = keyID ;
2020-04-25 01:36:08 +00:00
}
2020-04-26 01:09:03 +00:00
int tagsetID = 2 * keyID ;
if ( newKey )
2020-03-01 10:25:50 +00:00
{
2020-06-30 17:00:41 +00:00
m_NextReceiveRatchet - > key = i2p : : transport : : transports . GetNextX25519KeysPair ( ) ;
2020-04-26 01:09:03 +00:00
m_NextReceiveRatchet - > newKey = true ;
tagsetID + + ;
2020-03-01 10:25:50 +00:00
}
2020-04-25 01:36:08 +00:00
else
2020-04-26 01:09:03 +00:00
m_NextReceiveRatchet - > newKey = false ;
if ( flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG )
memcpy ( m_NextReceiveRatchet - > remote , buf , 32 ) ;
2020-03-01 10:25:50 +00:00
2020-04-26 01:09:03 +00:00
uint8_t sharedSecret [ 32 ] , tagsetKey [ 32 ] ;
2020-06-30 17:00:41 +00:00
m_NextReceiveRatchet - > key - > Agree ( m_NextReceiveRatchet - > remote , sharedSecret ) ;
2020-04-26 01:09:03 +00:00
i2p : : crypto : : HKDF ( sharedSecret , nullptr , 0 , " XDHRatchetTagSet " , tagsetKey , 32 ) ; // tagsetKey = HKDF(sharedSecret, ZEROLEN, "XDHRatchetTagSet", 32)
2021-01-04 23:20:16 +00:00
auto newTagset = std : : make_shared < ReceiveRatchetTagSet > ( shared_from_this ( ) ) ;
2020-03-01 10:25:50 +00:00
newTagset - > SetTagSetID ( tagsetID ) ;
newTagset - > DHInitialize ( receiveTagset - > GetNextRootKey ( ) , tagsetKey ) ;
2020-04-26 01:09:03 +00:00
newTagset - > NextSessionTagRatchet ( ) ;
2020-06-13 00:42:54 +00:00
GenerateMoreReceiveTags ( newTagset , ( GetOwner ( ) & & GetOwner ( ) - > GetNumRatchetInboundTags ( ) > 0 ) ?
GetOwner ( ) - > GetNumRatchetInboundTags ( ) : ECIESX25519_MAX_NUM_GENERATED_TAGS ) ;
2020-05-01 01:27:35 +00:00
receiveTagset - > Expire ( ) ;
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " Garlic: Next receive tagset " , tagsetID , " created " ) ;
2020-03-01 10:25:50 +00:00
}
}
2020-04-26 23:27:31 +00:00
void ECIESX25519AEADRatchetSession : : NewNextSendRatchet ( )
{
if ( m_NextSendRatchet )
{
2020-04-27 13:35:02 +00:00
if ( ! m_NextSendRatchet - > newKey | | ! m_NextSendRatchet - > keyID )
2020-04-26 23:27:31 +00:00
{
2020-04-27 13:35:02 +00:00
m_NextSendRatchet - > keyID + + ;
m_NextSendRatchet - > newKey = true ;
2020-03-01 10:25:50 +00:00
}
2020-04-26 23:27:31 +00:00
else
2020-04-27 13:35:02 +00:00
m_NextSendRatchet - > newKey = false ;
2020-03-01 10:25:50 +00:00
}
2020-04-27 13:35:02 +00:00
else
m_NextSendRatchet . reset ( new DHRatchet ( ) ) ;
if ( m_NextSendRatchet - > newKey )
2020-06-30 19:05:17 +00:00
m_NextSendRatchet - > key = i2p : : transport : : transports . GetNextX25519KeysPair ( ) ;
2020-03-01 10:25:50 +00:00
2020-04-26 23:27:31 +00:00
m_SendForwardKey = true ;
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " Garlic: New send ratchet " , m_NextSendRatchet - > newKey ? " new " : " old " , " key " , m_NextSendRatchet - > keyID , " created " ) ;
2020-03-01 10:25:50 +00:00
}
2020-11-06 02:21:46 +00:00
bool ECIESX25519AEADRatchetSession : : NewOutgoingSessionMessage ( const uint8_t * payload , size_t len , uint8_t * out , size_t outLen , bool isStatic )
2020-03-01 10:25:50 +00:00
{
// we are Alice, bpk is m_RemoteStaticKey
size_t offset = 0 ;
if ( ! GenerateEphemeralKeysAndEncode ( out + offset ) )
{
2020-01-16 21:34:13 +00:00
LogPrint ( eLogError , " Garlic: Can't encode elligator " ) ;
2020-03-01 10:25:50 +00:00
return false ;
}
offset + = 32 ;
// KDF1
2021-03-03 20:30:13 +00:00
i2p : : crypto : : InitNoiseIKState ( GetNoiseState ( ) , m_RemoteStaticKey ) ; // bpk
2020-06-30 17:00:41 +00:00
MixHash ( m_EphemeralKeys - > GetPublicKey ( ) , 32 ) ; // h = SHA256(h || aepk)
2020-03-01 10:25:50 +00:00
uint8_t sharedSecret [ 32 ] ;
2021-01-01 20:03:11 +00:00
if ( ! m_EphemeralKeys - > Agree ( m_RemoteStaticKey , sharedSecret ) ) // x25519(aesk, bpk)
{
LogPrint ( eLogWarning , " Garlic: Incorrect Bob static key " ) ;
return false ;
2021-11-27 20:30:35 +00:00
}
2020-11-05 20:27:37 +00:00
MixKey ( sharedSecret ) ;
2020-11-06 02:21:46 +00:00
// encrypt flags/static key section
2020-03-01 10:25:50 +00:00
uint8_t nonce [ 12 ] ;
2020-02-05 20:48:51 +00:00
CreateNonce ( 0 , nonce ) ;
2020-11-06 02:21:46 +00:00
const uint8_t * fs ;
if ( isStatic )
2020-11-14 23:28:50 +00:00
fs = GetOwner ( ) - > GetEncryptionPublicKey ( i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ) ;
2020-11-06 02:21:46 +00:00
else
{
2021-11-27 20:30:35 +00:00
memset ( out + offset , 0 , 32 ) ; // all zeros flags section
2020-11-06 02:21:46 +00:00
fs = out + offset ;
}
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( fs , 32 , m_H , 32 , m_CK + 32 , nonce , out + offset , 48 , true ) ) // encrypt
2020-01-16 21:34:13 +00:00
{
2020-11-06 02:21:46 +00:00
LogPrint ( eLogWarning , " Garlic: Flags/static section AEAD encryption failed " ) ;
2020-01-16 21:34:13 +00:00
return false ;
}
2021-11-27 20:30:35 +00:00
2020-03-01 10:25:50 +00:00
MixHash ( out + offset , 48 ) ; // h = SHA256(h || ciphertext)
offset + = 48 ;
// KDF2
2020-11-06 02:21:46 +00:00
if ( isStatic )
2021-11-27 20:30:35 +00:00
{
2021-08-31 22:51:40 +00:00
GetOwner ( ) - > Decrypt ( m_RemoteStaticKey , sharedSecret , i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ) ; // x25519 (ask, bpk)
2021-11-27 20:30:35 +00:00
MixKey ( sharedSecret ) ;
2020-11-06 02:21:46 +00:00
}
else
CreateNonce ( 1 , nonce ) ;
2020-01-17 00:33:00 +00:00
// encrypt payload
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( payload , len , m_H , 32 , m_CK + 32 , nonce , out + offset , len + 16 , true ) ) // encrypt
{
LogPrint ( eLogWarning , " Garlic: Payload section AEAD encryption failed " ) ;
return false ;
}
2021-11-27 20:30:35 +00:00
2020-01-29 20:54:11 +00:00
m_State = eSessionStateNewSessionSent ;
2020-11-06 02:21:46 +00:00
if ( isStatic )
2021-11-27 20:30:35 +00:00
{
2020-11-06 02:21:46 +00:00
MixHash ( out + offset , len + 16 ) ; // h = SHA256(h || ciphertext)
if ( GetOwner ( ) )
2021-01-05 01:15:48 +00:00
{
auto tagsetNsr = std : : make_shared < ReceiveRatchetTagSet > ( shared_from_this ( ) , true ) ;
InitNewSessionTagset ( tagsetNsr ) ;
2021-05-12 11:57:37 +00:00
tagsetNsr - > Expire ( ) ; // let non-replied session expire
2021-01-05 01:15:48 +00:00
GenerateMoreReceiveTags ( tagsetNsr , ECIESX25519_NSR_NUM_GENERATED_TAGS ) ;
2021-11-27 20:30:35 +00:00
}
2020-11-06 02:21:46 +00:00
}
2020-03-01 10:25:50 +00:00
return true ;
}
2021-11-27 20:30:35 +00:00
2020-03-01 10:25:50 +00:00
bool ECIESX25519AEADRatchetSession : : NewSessionReplyMessage ( const uint8_t * payload , size_t len , uint8_t * out , size_t outLen )
{
// we are Bob
2021-01-05 01:15:48 +00:00
m_NSRSendTagset = std : : make_shared < RatchetTagSet > ( ) ;
InitNewSessionTagset ( m_NSRSendTagset ) ;
2020-06-04 22:19:38 +00:00
uint64_t tag = m_NSRSendTagset - > GetNextSessionTag ( ) ;
2020-03-01 10:25:50 +00:00
size_t offset = 0 ;
memcpy ( out + offset , & tag , 8 ) ;
offset + = 8 ;
if ( ! GenerateEphemeralKeysAndEncode ( out + offset ) ) // bepk
{
2020-01-17 19:11:15 +00:00
LogPrint ( eLogError , " Garlic: Can't encode elligator " ) ;
2020-03-01 10:25:50 +00:00
return false ;
2020-01-22 16:27:47 +00:00
}
2021-10-18 10:09:56 +00:00
memcpy ( m_NSREncodedKey , out + offset , 32 ) ; // for possible next NSR
2020-04-28 22:23:13 +00:00
memcpy ( m_NSRH , m_H , 32 ) ;
2020-03-01 10:25:50 +00:00
offset + = 32 ;
// KDF for Reply Key Section
MixHash ( ( const uint8_t * ) & tag , 8 ) ; // h = SHA256(h || tag)
2020-06-30 17:00:41 +00:00
MixHash ( m_EphemeralKeys - > GetPublicKey ( ) , 32 ) ; // h = SHA256(h || bepk)
2020-03-01 10:25:50 +00:00
uint8_t sharedSecret [ 32 ] ;
2021-01-01 20:03:11 +00:00
if ( ! m_EphemeralKeys - > Agree ( m_Aepk , sharedSecret ) ) // sharedSecret = x25519(besk, aepk)
{
LogPrint ( eLogWarning , " Garlic: Incorrect Alice ephemeral key " ) ;
return false ;
2021-11-27 20:30:35 +00:00
}
2020-11-05 20:27:37 +00:00
MixKey ( sharedSecret ) ;
2021-01-01 20:03:11 +00:00
if ( ! m_EphemeralKeys - > Agree ( m_RemoteStaticKey , sharedSecret ) ) // sharedSecret = x25519(besk, apk)
{
LogPrint ( eLogWarning , " Garlic: Incorrect Alice static key " ) ;
return false ;
2021-11-27 20:30:35 +00:00
}
2020-11-05 20:27:37 +00:00
MixKey ( sharedSecret ) ;
2020-01-31 00:30:30 +00:00
uint8_t nonce [ 12 ] ;
2020-02-05 20:48:51 +00:00
CreateNonce ( 0 , nonce ) ;
2020-03-01 10:25:50 +00:00
// calculate hash for zero length
2020-04-28 22:23:13 +00:00
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( nonce /* can be anything */ , 0 , m_H , 32 , m_CK + 32 , nonce , out + offset , 16 , true ) ) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
2020-01-17 19:11:15 +00:00
{
LogPrint ( eLogWarning , " Garlic: Reply key section AEAD encryption failed " ) ;
return false ;
}
2020-03-01 10:25:50 +00:00
MixHash ( out + offset , 16 ) ; // h = SHA256(h || ciphertext)
offset + = 16 ;
// KDF for payload
uint8_t keydata [ 64 ] ;
i2p : : crypto : : HKDF ( m_CK , nullptr , 0 , " " , keydata ) ; // keydata = HKDF(chainKey, ZEROLEN, "", 64)
2020-02-09 02:51:02 +00:00
// k_ab = keydata[0:31], k_ba = keydata[32:63]
2021-01-04 23:20:16 +00:00
auto receiveTagset = std : : make_shared < ReceiveRatchetTagSet > ( shared_from_this ( ) ) ;
2020-03-01 10:25:50 +00:00
receiveTagset - > DHInitialize ( m_CK , keydata ) ; // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
2020-04-25 01:36:08 +00:00
receiveTagset - > NextSessionTagRatchet ( ) ;
2021-01-04 23:20:16 +00:00
m_SendTagset = std : : make_shared < RatchetTagSet > ( ) ;
2020-03-01 10:25:50 +00:00
m_SendTagset - > DHInitialize ( m_CK , keydata + 32 ) ; // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
m_SendTagset - > NextSessionTagRatchet ( ) ;
2020-06-13 00:42:54 +00:00
GenerateMoreReceiveTags ( receiveTagset , ( GetOwner ( ) & & GetOwner ( ) - > GetNumRatchetInboundTags ( ) > 0 ) ?
GetOwner ( ) - > GetNumRatchetInboundTags ( ) : ECIESX25519_MIN_NUM_GENERATED_TAGS ) ;
2020-03-01 10:25:50 +00:00
i2p : : crypto : : HKDF ( keydata + 32 , nullptr , 0 , " AttachPayloadKDF " , m_NSRKey , 32 ) ; // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
// encrypt payload
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( payload , len , m_H , 32 , m_NSRKey , nonce , out + offset , len + 16 , true ) ) // encrypt
2020-01-17 19:11:15 +00:00
{
2020-04-03 01:48:39 +00:00
LogPrint ( eLogWarning , " Garlic: NSR payload section AEAD encryption failed " ) ;
2020-01-17 19:11:15 +00:00
return false ;
}
2020-04-03 01:48:39 +00:00
m_State = eSessionStateNewSessionReplySent ;
2020-05-23 14:20:22 +00:00
m_SessionCreatedTimestamp = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2021-11-27 20:30:35 +00:00
2020-03-01 10:25:50 +00:00
return true ;
}
2021-11-27 20:30:35 +00:00
2020-04-03 01:48:39 +00:00
bool ECIESX25519AEADRatchetSession : : NextNewSessionReplyMessage ( const uint8_t * payload , size_t len , uint8_t * out , size_t outLen )
2020-03-01 10:25:50 +00:00
{
// we are Bob and sent NSR already
2020-06-04 22:19:38 +00:00
uint64_t tag = m_NSRSendTagset - > GetNextSessionTag ( ) ; // next tag
2020-03-01 10:25:50 +00:00
memcpy ( out , & tag , 8 ) ;
2020-04-28 22:23:13 +00:00
memcpy ( out + 8 , m_NSREncodedKey , 32 ) ;
2020-03-01 10:25:50 +00:00
// recalculate h with new tag
2020-04-28 22:23:13 +00:00
memcpy ( m_H , m_NSRH , 32 ) ;
MixHash ( ( const uint8_t * ) & tag , 8 ) ; // h = SHA256(h || tag)
2020-06-30 17:00:41 +00:00
MixHash ( m_EphemeralKeys - > GetPublicKey ( ) , 32 ) ; // h = SHA256(h || bepk)
2020-04-03 01:48:39 +00:00
uint8_t nonce [ 12 ] ;
2020-03-01 10:25:50 +00:00
CreateNonce ( 0 , nonce ) ;
2020-04-28 22:23:13 +00:00
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( nonce /* can be anything */ , 0 , m_H , 32 , m_CK + 32 , nonce , out + 40 , 16 , true ) ) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
{
LogPrint ( eLogWarning , " Garlic: Reply key section AEAD encryption failed " ) ;
return false ;
}
2020-03-01 10:25:50 +00:00
MixHash ( out + 40 , 16 ) ; // h = SHA256(h || ciphertext)
2020-04-03 01:48:39 +00:00
// encrypt payload
2020-03-01 10:25:50 +00:00
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( payload , len , m_H , 32 , m_NSRKey , nonce , out + 56 , len + 16 , true ) ) // encrypt
2020-04-03 01:48:39 +00:00
{
LogPrint ( eLogWarning , " Garlic: Next NSR payload section AEAD encryption failed " ) ;
return false ;
}
return true ;
2020-03-01 10:25:50 +00:00
}
bool ECIESX25519AEADRatchetSession : : HandleNewOutgoingSessionReply ( uint8_t * buf , size_t len )
{
2020-01-29 17:54:26 +00:00
// we are Alice
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " Garlic: Reply received " ) ;
2020-01-29 17:54:26 +00:00
const uint8_t * tag = buf ;
buf + = 8 ; len - = 8 ; // tag
2020-03-01 10:25:50 +00:00
uint8_t bepk [ 32 ] ; // Bob's ephemeral key
2020-01-29 17:54:26 +00:00
if ( ! i2p : : crypto : : GetElligator ( ) - > Decode ( buf , bepk ) )
2020-03-01 10:25:50 +00:00
{
2020-01-29 17:54:26 +00:00
LogPrint ( eLogError , " Garlic: Can't decode elligator " ) ;
2020-03-01 10:25:50 +00:00
return false ;
}
2020-01-29 17:54:26 +00:00
buf + = 32 ; len - = 32 ;
2020-03-01 10:25:50 +00:00
// KDF for Reply Key Section
2021-03-03 20:30:13 +00:00
i2p : : util : : SaveStateHelper < i2p : : crypto : : NoiseSymmetricState > s ( GetNoiseState ( ) ) ; // restore noise state on exit
2020-03-01 10:25:50 +00:00
MixHash ( tag , 8 ) ; // h = SHA256(h || tag)
MixHash ( bepk , 32 ) ; // h = SHA256(h || bepk)
uint8_t sharedSecret [ 32 ] ;
2021-01-01 20:03:11 +00:00
if ( ! m_EphemeralKeys - > Agree ( bepk , sharedSecret ) ) // sharedSecret = x25519(aesk, bepk)
{
LogPrint ( eLogWarning , " Garlic: Incorrect Bob ephemeral key " ) ;
return false ;
2021-11-27 20:30:35 +00:00
}
2020-12-27 16:18:53 +00:00
MixKey ( sharedSecret ) ;
2021-08-31 22:51:40 +00:00
GetOwner ( ) - > Decrypt ( bepk , sharedSecret , i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ) ; // x25519 (ask, bepk)
2020-12-27 16:18:53 +00:00
MixKey ( sharedSecret ) ;
2020-01-31 00:30:30 +00:00
uint8_t nonce [ 12 ] ;
2020-02-05 20:48:51 +00:00
CreateNonce ( 0 , nonce ) ;
2020-03-01 10:25:50 +00:00
// calculate hash for zero length
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( buf , 0 , m_H , 32 , m_CK + 32 , nonce , sharedSecret /* can be anything */ , 0 , false ) ) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only
2020-01-29 17:54:26 +00:00
{
LogPrint ( eLogWarning , " Garlic: Reply key section AEAD decryption failed " ) ;
return false ;
}
2020-03-01 10:25:50 +00:00
MixHash ( buf , 16 ) ; // h = SHA256(h || ciphertext)
2020-01-29 17:54:26 +00:00
buf + = 16 ; len - = 16 ;
// KDF for payload
2020-03-01 10:25:50 +00:00
uint8_t keydata [ 64 ] ;
i2p : : crypto : : HKDF ( m_CK , nullptr , 0 , " " , keydata ) ; // keydata = HKDF(chainKey, ZEROLEN, "", 64)
2020-04-29 02:03:13 +00:00
if ( m_State = = eSessionStateNewSessionSent )
2020-03-01 10:25:50 +00:00
{
2020-12-27 16:18:53 +00:00
// only first time, then we keep using existing tagsets
2020-03-01 10:25:50 +00:00
// k_ab = keydata[0:31], k_ba = keydata[32:63]
2021-01-04 23:20:16 +00:00
m_SendTagset = std : : make_shared < RatchetTagSet > ( ) ;
2020-03-01 10:25:50 +00:00
m_SendTagset - > DHInitialize ( m_CK , keydata ) ; // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
2020-04-29 02:03:13 +00:00
m_SendTagset - > NextSessionTagRatchet ( ) ;
2021-01-04 23:20:16 +00:00
auto receiveTagset = std : : make_shared < ReceiveRatchetTagSet > ( shared_from_this ( ) ) ;
2020-03-01 10:25:50 +00:00
receiveTagset - > DHInitialize ( m_CK , keydata + 32 ) ; // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
2020-04-29 02:03:13 +00:00
receiveTagset - > NextSessionTagRatchet ( ) ;
2020-06-13 00:42:54 +00:00
GenerateMoreReceiveTags ( receiveTagset , ( GetOwner ( ) & & GetOwner ( ) - > GetNumRatchetInboundTags ( ) > 0 ) ?
GetOwner ( ) - > GetNumRatchetInboundTags ( ) : ECIESX25519_MIN_NUM_GENERATED_TAGS ) ;
2020-03-01 10:25:50 +00:00
}
i2p : : crypto : : HKDF ( keydata + 32 , nullptr , 0 , " AttachPayloadKDF " , keydata , 32 ) ; // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
2020-01-29 17:54:26 +00:00
// decrypt payload
2020-03-01 10:25:50 +00:00
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( buf , len - 16 , m_H , 32 , keydata , nonce , buf , len - 16 , false ) ) // decrypt
2020-01-29 17:54:26 +00:00
{
LogPrint ( eLogWarning , " Garlic: Payload section AEAD decryption failed " ) ;
return false ;
}
2020-04-29 02:03:13 +00:00
if ( m_State = = eSessionStateNewSessionSent )
2020-03-01 10:25:50 +00:00
{
2020-04-29 02:03:13 +00:00
m_State = eSessionStateEstablished ;
2020-12-27 16:18:53 +00:00
//m_EphemeralKeys = nullptr; // TODO: delete after a while
2020-05-23 14:20:22 +00:00
m_SessionCreatedTimestamp = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2020-04-29 02:03:13 +00:00
GetOwner ( ) - > AddECIESx25519Session ( m_RemoteStaticKey , shared_from_this ( ) ) ;
}
2020-03-01 10:25:50 +00:00
HandlePayload ( buf , len - 16 , nullptr , 0 ) ;
2020-01-29 17:54:26 +00:00
2020-04-19 19:00:51 +00:00
// we have received reply to NS with LeaseSet in it
SetLeaseSetUpdateStatus ( eLeaseSetUpToDate ) ;
SetLeaseSetUpdateMsgID ( 0 ) ;
2020-03-01 10:25:50 +00:00
return true ;
}
2020-01-21 19:40:23 +00:00
2020-02-05 20:48:51 +00:00
bool ECIESX25519AEADRatchetSession : : NewExistingSessionMessage ( const uint8_t * payload , size_t len , uint8_t * out , size_t outLen )
2020-02-03 21:21:07 +00:00
{
uint8_t nonce [ 12 ] ;
2020-04-24 19:46:02 +00:00
auto index = m_SendTagset - > GetNextIndex ( ) ;
2020-02-09 02:51:02 +00:00
CreateNonce ( index , nonce ) ; // tag's index
2020-04-24 19:46:02 +00:00
uint64_t tag = m_SendTagset - > GetNextSessionTag ( ) ;
2020-12-26 22:18:29 +00:00
if ( ! tag )
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset " ) ;
2020-12-26 22:18:29 +00:00
if ( GetOwner ( ) )
GetOwner ( ) - > RemoveECIESx25519Session ( m_RemoteStaticKey ) ;
return false ;
2021-11-27 20:30:35 +00:00
}
2020-02-05 20:48:51 +00:00
memcpy ( out , & tag , 8 ) ;
2020-02-03 21:21:07 +00:00
// ad = The session tag, 8 bytes
// ciphertext = ENCRYPT(k, n, payload, ad)
2020-03-07 23:46:40 +00:00
uint8_t key [ 32 ] ;
2020-04-24 19:46:02 +00:00
m_SendTagset - > GetSymmKey ( index , key ) ;
2020-03-07 23:46:40 +00:00
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( payload , len , out , 8 , key , nonce , out + 8 , outLen - 8 , true ) ) // encrypt
2020-02-05 20:48:51 +00:00
{
LogPrint ( eLogWarning , " Garlic: Payload section AEAD encryption failed " ) ;
return false ;
2020-03-01 10:25:50 +00:00
}
2020-04-26 23:27:31 +00:00
if ( index > = ECIESX25519_TAGSET_MAX_NUM_TAGS & & ! m_SendForwardKey )
2020-03-01 10:25:50 +00:00
NewNextSendRatchet ( ) ;
2020-02-05 20:48:51 +00:00
return true ;
}
2020-03-01 10:25:50 +00:00
bool ECIESX25519AEADRatchetSession : : HandleExistingSessionMessage ( uint8_t * buf , size_t len ,
2021-01-04 23:20:16 +00:00
std : : shared_ptr < ReceiveRatchetTagSet > receiveTagset , int index )
2020-02-05 20:48:51 +00:00
{
uint8_t nonce [ 12 ] ;
CreateNonce ( index , nonce ) ; // tag's index
2020-03-01 10:25:50 +00:00
len - = 8 ; // tag
2020-05-12 22:30:04 +00:00
uint8_t * payload = buf + 8 ;
2020-03-07 23:46:40 +00:00
uint8_t key [ 32 ] ;
2020-04-25 01:36:08 +00:00
receiveTagset - > GetSymmKey ( index , key ) ;
2020-05-12 22:30:04 +00:00
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( payload , len - 16 , buf , 8 , key , nonce , payload , len - 16 , false ) ) // decrypt
2020-02-03 21:21:07 +00:00
{
LogPrint ( eLogWarning , " Garlic: Payload section AEAD decryption failed " ) ;
return false ;
2020-03-01 10:25:50 +00:00
}
HandlePayload ( payload , len - 16 , receiveTagset , index ) ;
2020-06-13 00:42:54 +00:00
if ( GetOwner ( ) )
2021-11-27 20:30:35 +00:00
{
2020-06-13 00:42:54 +00:00
int moreTags = 0 ;
if ( GetOwner ( ) - > GetNumRatchetInboundTags ( ) > 0 ) // override in settings?
{
if ( receiveTagset - > GetNextIndex ( ) - index < GetOwner ( ) - > GetNumRatchetInboundTags ( ) / 2 )
moreTags = GetOwner ( ) - > GetNumRatchetInboundTags ( ) ;
2020-12-21 00:52:06 +00:00
index - = GetOwner ( ) - > GetNumRatchetInboundTags ( ) ; // trim behind
2020-06-13 00:42:54 +00:00
}
else
2021-11-27 20:30:35 +00:00
{
2020-06-13 00:42:54 +00:00
moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + ( index > > 2 ) ; // N/4
if ( moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS ) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS ;
moreTags - = ( receiveTagset - > GetNextIndex ( ) - index ) ;
2020-12-21 00:52:06 +00:00
index - = ECIESX25519_MAX_NUM_GENERATED_TAGS ; // trim behind
2021-11-27 20:30:35 +00:00
}
2020-06-13 00:42:54 +00:00
if ( moreTags > 0 )
GenerateMoreReceiveTags ( receiveTagset , moreTags ) ;
2020-12-21 00:52:06 +00:00
if ( index > 0 )
receiveTagset - > SetTrimBehind ( index ) ;
2021-11-27 20:30:35 +00:00
}
2020-02-03 21:21:07 +00:00
return true ;
}
2020-03-01 10:25:50 +00:00
bool ECIESX25519AEADRatchetSession : : HandleNextMessage ( uint8_t * buf , size_t len ,
2021-01-04 23:20:16 +00:00
std : : shared_ptr < ReceiveRatchetTagSet > receiveTagset , int index )
2020-01-29 20:54:11 +00:00
{
2020-02-20 20:44:09 +00:00
m_LastActivityTimestamp = i2p : : util : : GetSecondsSinceEpoch ( ) ;
2020-01-29 20:54:11 +00:00
switch ( m_State )
{
2020-04-03 01:48:39 +00:00
case eSessionStateNewSessionReplySent :
m_State = eSessionStateEstablished ;
2020-06-04 22:19:38 +00:00
m_NSRSendTagset = nullptr ;
2020-06-30 17:00:41 +00:00
m_EphemeralKeys = nullptr ;
2020-04-03 01:48:39 +00:00
# if (__cplusplus >= 201703L) // C++ 17 or higher
2020-03-01 10:25:50 +00:00
[[fallthrough]] ;
# endif
2020-02-03 21:21:07 +00:00
case eSessionStateEstablished :
2020-12-27 16:18:53 +00:00
if ( receiveTagset - > IsNS ( ) )
2021-11-27 20:30:35 +00:00
{
// our of sequence NSR
2021-11-27 19:53:53 +00:00
LogPrint ( eLogDebug , " Garlic: Check for out of order NSR with index " , index ) ;
2020-12-27 16:18:53 +00:00
if ( receiveTagset - > GetNextIndex ( ) - index < ECIESX25519_NSR_NUM_GENERATED_TAGS / 2 )
GenerateMoreReceiveTags ( receiveTagset , ECIESX25519_NSR_NUM_GENERATED_TAGS ) ;
return HandleNewOutgoingSessionReply ( buf , len ) ;
2021-11-27 20:30:35 +00:00
}
2020-12-27 16:18:53 +00:00
else
return HandleExistingSessionMessage ( buf , len , receiveTagset , index ) ;
2020-01-29 20:54:11 +00:00
case eSessionStateNew :
2020-02-03 21:21:07 +00:00
return HandleNewIncomingSession ( buf , len ) ;
2020-01-29 20:54:11 +00:00
case eSessionStateNewSessionSent :
2020-02-03 21:21:07 +00:00
return HandleNewOutgoingSessionReply ( buf , len ) ;
2020-01-29 20:54:11 +00:00
default :
return false ;
}
return true ;
}
2021-11-27 20:30:35 +00:00
2020-03-01 10:25:50 +00:00
std : : shared_ptr < I2NPMessage > ECIESX25519AEADRatchetSession : : WrapSingleMessage ( std : : shared_ptr < const I2NPMessage > msg )
{
2021-07-18 22:45:08 +00:00
uint8_t * payload = GetOwner ( ) - > GetPayloadBuffer ( ) ;
if ( ! payload ) return nullptr ;
size_t len = CreatePayload ( msg , m_State ! = eSessionStateEstablished , payload ) ;
2020-05-16 23:10:17 +00:00
if ( ! len ) return nullptr ;
2020-04-30 19:38:15 +00:00
auto m = NewI2NPMessage ( len + 100 ) ; // 96 + 4
m - > Align ( 12 ) ; // in order to get buf aligned to 16 (12 + 4)
uint8_t * buf = m - > GetPayload ( ) + 4 ; // 4 bytes for length
2020-03-01 10:25:50 +00:00
switch ( m_State )
{
2020-02-05 20:48:51 +00:00
case eSessionStateEstablished :
2021-07-18 22:45:08 +00:00
if ( ! NewExistingSessionMessage ( payload , len , buf , m - > maxLen ) )
2020-02-05 20:48:51 +00:00
return nullptr ;
len + = 24 ;
2020-03-01 10:25:50 +00:00
break ;
case eSessionStateNew :
2021-07-18 22:45:08 +00:00
if ( ! NewOutgoingSessionMessage ( payload , len , buf , m - > maxLen ) )
2020-03-01 10:25:50 +00:00
return nullptr ;
len + = 96 ;
break ;
case eSessionStateNewSessionReceived :
2021-07-18 22:45:08 +00:00
if ( ! NewSessionReplyMessage ( payload , len , buf , m - > maxLen ) )
2020-03-01 10:25:50 +00:00
return nullptr ;
len + = 72 ;
break ;
2020-04-03 01:48:39 +00:00
case eSessionStateNewSessionReplySent :
2021-07-18 22:45:08 +00:00
if ( ! NextNewSessionReplyMessage ( payload , len , buf , m - > maxLen ) )
2020-03-01 10:25:50 +00:00
return nullptr ;
len + = 72 ;
break ;
2020-11-06 02:21:46 +00:00
case eSessionStateOneTime :
2021-07-18 22:45:08 +00:00
if ( ! NewOutgoingSessionMessage ( payload , len , buf , m - > maxLen , false ) )
2020-11-06 02:21:46 +00:00
return nullptr ;
len + = 96 ;
2021-11-27 20:30:35 +00:00
break ;
2020-03-01 10:25:50 +00:00
default :
return nullptr ;
}
htobe32buf ( m - > GetPayload ( ) , len ) ;
2020-01-17 16:21:41 +00:00
m - > len + = len + 4 ;
m - > FillI2NPMessageHeader ( eI2NPGarlic ) ;
return m ;
2020-03-01 10:25:50 +00:00
}
2020-01-17 16:21:41 +00:00
2021-07-15 22:18:55 +00:00
std : : shared_ptr < I2NPMessage > ECIESX25519AEADRatchetSession : : WrapOneTimeMessage ( std : : shared_ptr < const I2NPMessage > msg )
2020-11-06 02:21:46 +00:00
{
2021-07-15 22:18:55 +00:00
m_State = eSessionStateOneTime ;
2020-11-06 02:21:46 +00:00
return WrapSingleMessage ( msg ) ;
2021-11-27 20:30:35 +00:00
}
2021-07-18 22:45:08 +00:00
size_t ECIESX25519AEADRatchetSession : : CreatePayload ( std : : shared_ptr < const I2NPMessage > msg , bool first , uint8_t * payload )
2020-03-01 10:25:50 +00:00
{
2020-03-08 22:13:41 +00:00
uint64_t ts = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
2020-03-01 10:25:50 +00:00
size_t payloadLen = 0 ;
if ( first ) payloadLen + = 7 ; // datatime
2020-07-29 21:47:46 +00:00
if ( msg )
2021-11-27 20:30:35 +00:00
{
2020-07-29 21:47:46 +00:00
payloadLen + = msg - > GetPayloadLength ( ) + 13 ;
if ( m_Destination ) payloadLen + = 32 ;
2021-11-27 20:30:35 +00:00
}
2021-01-24 02:25:52 +00:00
if ( GetLeaseSetUpdateStatus ( ) = = eLeaseSetSubmitted & & ts > GetLeaseSetSubmissionTime ( ) + LEASET_CONFIRMATION_TIMEOUT )
{
// resubmit non-confirmed LeaseSet
SetLeaseSetUpdateStatus ( eLeaseSetUpdated ) ;
SetSharedRoutingPath ( nullptr ) ; // invalidate path since leaseset was not confirmed
}
auto leaseSet = ( GetLeaseSetUpdateStatus ( ) = = eLeaseSetUpdated ) ? GetOwner ( ) - > GetLeaseSet ( ) : nullptr ;
2020-03-08 22:13:41 +00:00
if ( leaseSet )
2020-03-01 10:25:50 +00:00
{
payloadLen + = leaseSet - > GetBufferLen ( ) + DATABASE_STORE_HEADER_SIZE + 13 ;
if ( ! first )
2020-04-19 19:00:51 +00:00
{
// ack request
SetLeaseSetUpdateStatus ( eLeaseSetSubmitted ) ;
2020-04-24 19:46:02 +00:00
SetLeaseSetUpdateMsgID ( m_SendTagset - > GetNextIndex ( ) ) ;
2020-04-19 19:00:51 +00:00
SetLeaseSetSubmissionTime ( ts ) ;
2020-03-01 10:25:50 +00:00
payloadLen + = 4 ;
}
}
2020-03-26 23:03:38 +00:00
if ( m_AckRequests . size ( ) > 0 )
payloadLen + = m_AckRequests . size ( ) * 4 + 3 ;
2020-04-26 01:09:03 +00:00
if ( m_SendReverseKey )
2020-03-01 10:25:50 +00:00
{
2020-04-26 01:09:03 +00:00
payloadLen + = 6 ;
if ( m_NextReceiveRatchet - > newKey ) payloadLen + = 32 ;
2020-03-01 10:25:50 +00:00
}
2020-04-26 23:27:31 +00:00
if ( m_SendForwardKey )
{
payloadLen + = 6 ;
if ( m_NextSendRatchet - > newKey ) payloadLen + = 32 ;
2020-05-16 23:10:17 +00:00
}
uint8_t paddingSize = 0 ;
2020-07-15 20:20:35 +00:00
if ( payloadLen | | ts > m_LastSentTimestamp + ECIESX25519_SEND_INACTIVITY_TIMEOUT )
2020-03-01 10:25:50 +00:00
{
2020-05-19 23:03:12 +00:00
int delta = ( int ) ECIESX25519_OPTIMAL_PAYLOAD_SIZE - ( int ) payloadLen ;
if ( delta < 0 | | delta > 3 ) // don't create padding if we are close to optimal size
{
2020-06-14 01:24:16 +00:00
paddingSize = m_PaddingSizes [ m_NextPaddingSize + + ] & 0x0F ; // 0 - 15
if ( m_NextPaddingSize > = 32 )
{
2021-11-27 20:30:35 +00:00
RAND_bytes ( m_PaddingSizes , 32 ) ;
2020-06-14 01:24:16 +00:00
m_NextPaddingSize = 0 ;
2021-11-27 20:30:35 +00:00
}
2020-05-19 23:03:12 +00:00
if ( delta > 3 )
2020-03-01 10:25:50 +00:00
{
2020-05-19 23:03:12 +00:00
delta - = 3 ;
2020-03-01 10:25:50 +00:00
if ( paddingSize > = delta ) paddingSize % = delta ;
}
2020-05-19 23:03:12 +00:00
paddingSize + + ;
2020-03-01 10:25:50 +00:00
payloadLen + = paddingSize + 3 ;
}
}
2020-07-15 20:20:35 +00:00
if ( payloadLen )
2021-07-18 22:45:08 +00:00
{
if ( payloadLen > I2NP_MAX_MESSAGE_SIZE )
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " Garlic: Payload length " , payloadLen , " is too long " ) ;
2021-07-18 22:45:08 +00:00
return 0 ;
2021-11-27 20:30:35 +00:00
}
2020-07-15 20:20:35 +00:00
m_LastSentTimestamp = ts ;
size_t offset = 0 ;
// DateTime
if ( first )
2020-03-01 10:25:50 +00:00
{
2021-07-18 22:45:08 +00:00
payload [ offset ] = eECIESx25519BlkDateTime ; offset + + ;
htobe16buf ( payload + offset , 4 ) ; offset + = 2 ;
htobe32buf ( payload + offset , ts / 1000 ) ; offset + = 4 ; // in seconds
2020-03-01 10:25:50 +00:00
}
2020-07-15 20:20:35 +00:00
// LeaseSet
if ( leaseSet )
2020-03-26 23:03:38 +00:00
{
2021-07-18 22:45:08 +00:00
offset + = CreateLeaseSetClove ( leaseSet , ts , payload + offset , payloadLen - offset ) ;
2020-07-15 20:20:35 +00:00
if ( ! first )
{
// ack request
2021-07-18 22:45:08 +00:00
payload [ offset ] = eECIESx25519BlkAckRequest ; offset + + ;
htobe16buf ( payload + offset , 1 ) ; offset + = 2 ;
payload [ offset ] = 0 ; offset + + ; // flags
2020-07-15 20:20:35 +00:00
}
2020-03-01 10:25:50 +00:00
}
2020-07-15 20:20:35 +00:00
// msg
2020-11-09 20:35:50 +00:00
if ( msg )
2021-07-18 22:45:08 +00:00
offset + = CreateGarlicClove ( msg , payload + offset , payloadLen - offset ) ;
2020-07-15 20:20:35 +00:00
// ack
if ( m_AckRequests . size ( ) > 0 )
2020-03-01 10:25:50 +00:00
{
2021-07-18 22:45:08 +00:00
payload [ offset ] = eECIESx25519BlkAck ; offset + + ;
htobe16buf ( payload + offset , m_AckRequests . size ( ) * 4 ) ; offset + = 2 ;
2020-07-15 20:20:35 +00:00
for ( auto & it : m_AckRequests )
{
2021-07-18 22:45:08 +00:00
htobe16buf ( payload + offset , it . first ) ; offset + = 2 ;
htobe16buf ( payload + offset , it . second ) ; offset + = 2 ;
2020-07-15 20:20:35 +00:00
}
m_AckRequests . clear ( ) ;
2020-03-01 10:25:50 +00:00
}
2020-07-15 20:20:35 +00:00
// next keys
if ( m_SendReverseKey )
2020-03-01 10:25:50 +00:00
{
2021-07-18 22:45:08 +00:00
payload [ offset ] = eECIESx25519BlkNextKey ; offset + + ;
htobe16buf ( payload + offset , m_NextReceiveRatchet - > newKey ? 35 : 3 ) ; offset + = 2 ;
payload [ offset ] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG ;
2020-07-15 20:20:35 +00:00
int keyID = m_NextReceiveRatchet - > keyID - 1 ;
if ( m_NextReceiveRatchet - > newKey )
{
2021-07-18 22:45:08 +00:00
payload [ offset ] | = ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG ;
2020-07-15 20:20:35 +00:00
keyID + + ;
}
offset + + ; // flag
2021-07-18 22:45:08 +00:00
htobe16buf ( payload + offset , keyID ) ; offset + = 2 ; // keyid
2020-07-15 20:20:35 +00:00
if ( m_NextReceiveRatchet - > newKey )
{
2021-07-18 22:45:08 +00:00
memcpy ( payload + offset , m_NextReceiveRatchet - > key - > GetPublicKey ( ) , 32 ) ;
2020-07-15 20:20:35 +00:00
offset + = 32 ; // public key
}
m_SendReverseKey = false ;
2020-03-01 10:25:50 +00:00
}
2020-07-15 20:20:35 +00:00
if ( m_SendForwardKey )
2020-03-01 10:25:50 +00:00
{
2021-07-18 22:45:08 +00:00
payload [ offset ] = eECIESx25519BlkNextKey ; offset + + ;
htobe16buf ( payload + offset , m_NextSendRatchet - > newKey ? 35 : 3 ) ; offset + = 2 ;
payload [ offset ] = m_NextSendRatchet - > newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG ;
if ( ! m_NextSendRatchet - > keyID ) payload [ offset ] | = ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG ; // for first key only
2020-07-15 20:20:35 +00:00
offset + + ; // flag
2021-07-18 22:45:08 +00:00
htobe16buf ( payload + offset , m_NextSendRatchet - > keyID ) ; offset + = 2 ; // keyid
2020-07-15 20:20:35 +00:00
if ( m_NextSendRatchet - > newKey )
{
2021-07-18 22:45:08 +00:00
memcpy ( payload + offset , m_NextSendRatchet - > key - > GetPublicKey ( ) , 32 ) ;
2020-07-15 20:20:35 +00:00
offset + = 32 ; // public key
}
2020-03-01 10:25:50 +00:00
}
2020-07-15 20:20:35 +00:00
// padding
if ( paddingSize )
{
2021-07-18 22:45:08 +00:00
payload [ offset ] = eECIESx25519BlkPadding ; offset + + ;
htobe16buf ( payload + offset , paddingSize ) ; offset + = 2 ;
memset ( payload + offset , 0 , paddingSize ) ; offset + = paddingSize ;
2020-07-15 20:20:35 +00:00
}
2021-11-27 20:30:35 +00:00
}
2021-07-18 22:45:08 +00:00
return payloadLen ;
2020-03-01 10:25:50 +00:00
}
2020-07-29 21:47:46 +00:00
size_t ECIESX25519AEADRatchetSession : : CreateGarlicClove ( std : : shared_ptr < const I2NPMessage > msg , uint8_t * buf , size_t len )
2020-03-01 10:25:50 +00:00
{
if ( ! msg ) return 0 ;
uint16_t cloveSize = msg - > GetPayloadLength ( ) + 9 + 1 ;
2020-07-29 21:47:46 +00:00
if ( m_Destination ) cloveSize + = 32 ;
2020-03-01 10:25:50 +00:00
if ( ( int ) len < cloveSize + 3 ) return 0 ;
buf [ 0 ] = eECIESx25519BlkGalicClove ; // clove type
htobe16buf ( buf + 1 , cloveSize ) ; // size
2020-01-30 16:48:32 +00:00
buf + = 3 ;
2020-07-29 21:47:46 +00:00
if ( m_Destination )
2020-01-30 16:48:32 +00:00
{
* buf = ( eGarlicDeliveryTypeDestination < < 5 ) ;
memcpy ( buf + 1 , * m_Destination , 32 ) ; buf + = 32 ;
}
else
2020-03-01 10:25:50 +00:00
* buf = 0 ;
2020-01-30 16:48:32 +00:00
buf + + ; // flag and delivery instructions
2020-03-01 10:25:50 +00:00
* buf = msg - > GetTypeID ( ) ; // I2NP msg type
htobe32buf ( buf + 1 , msg - > GetMsgID ( ) ) ; // msgID
htobe32buf ( buf + 5 , msg - > GetExpiration ( ) / 1000 ) ; // expiration in seconds
memcpy ( buf + 9 , msg - > GetPayload ( ) , msg - > GetPayloadLength ( ) ) ;
return cloveSize + 3 ;
}
2020-02-09 22:19:42 +00:00
2020-05-13 22:09:26 +00:00
size_t ECIESX25519AEADRatchetSession : : CreateLeaseSetClove ( std : : shared_ptr < const i2p : : data : : LocalLeaseSet > ls , uint64_t ts , uint8_t * buf , size_t len )
2020-03-01 10:25:50 +00:00
{
2020-05-13 22:09:26 +00:00
if ( ! ls | | ls - > GetStoreType ( ) ! = i2p : : data : : NETDB_STORE_TYPE_STANDARD_LEASESET2 )
{
LogPrint ( eLogError , " Garlic: Incorrect LeasetSet type to send " ) ;
return 0 ;
2020-03-01 10:25:50 +00:00
}
2020-05-13 22:09:26 +00:00
uint16_t cloveSize = 1 + 9 + DATABASE_STORE_HEADER_SIZE + ls - > GetBufferLen ( ) ; // to local
2020-03-08 22:13:41 +00:00
if ( ( int ) len < cloveSize + 3 ) return 0 ;
buf [ 0 ] = eECIESx25519BlkGalicClove ; // clove type
2020-03-01 10:25:50 +00:00
htobe16buf ( buf + 1 , cloveSize ) ; // size
2020-03-08 22:13:41 +00:00
buf + = 3 ;
2020-05-13 22:09:26 +00:00
* buf = 0 ; buf + + ; // flag and delivery instructions
* buf = eI2NPDatabaseStore ; buf + + ; // I2NP msg type
RAND_bytes ( buf , 4 ) ; buf + = 4 ; // msgID
2020-03-01 10:25:50 +00:00
htobe32buf ( buf , ( ts + I2NP_MESSAGE_EXPIRATION_TIMEOUT ) / 1000 ) ; buf + = 4 ; // expiration
2020-05-13 22:09:26 +00:00
// payload
memcpy ( buf + DATABASE_STORE_KEY_OFFSET , ls - > GetStoreHash ( ) , 32 ) ;
buf [ DATABASE_STORE_TYPE_OFFSET ] = i2p : : data : : NETDB_STORE_TYPE_STANDARD_LEASESET2 ;
2020-03-01 10:25:50 +00:00
memset ( buf + DATABASE_STORE_REPLY_TOKEN_OFFSET , 0 , 4 ) ; // replyToken = 0
2020-05-13 22:09:26 +00:00
buf + = DATABASE_STORE_HEADER_SIZE ;
memcpy ( buf , ls - > GetBuffer ( ) , ls - > GetBufferLen ( ) ) ;
2020-03-08 22:13:41 +00:00
return cloveSize + 3 ;
2020-05-13 22:09:26 +00:00
}
2020-03-01 10:25:50 +00:00
2021-01-04 23:20:16 +00:00
void ECIESX25519AEADRatchetSession : : GenerateMoreReceiveTags ( std : : shared_ptr < ReceiveRatchetTagSet > receiveTagset , int numTags )
2020-02-09 22:19:42 +00:00
{
2020-11-26 14:15:45 +00:00
if ( GetOwner ( ) )
2021-11-27 20:30:35 +00:00
{
2020-11-26 14:15:45 +00:00
for ( int i = 0 ; i < numTags ; i + + )
2021-11-27 20:30:35 +00:00
{
2020-12-26 22:18:29 +00:00
auto tag = GetOwner ( ) - > AddECIESx25519SessionNextTag ( receiveTagset ) ;
if ( ! tag )
{
2021-11-27 19:53:53 +00:00
LogPrint ( eLogError , " Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for receive tagset " ) ;
2020-12-26 22:18:29 +00:00
break ;
2021-11-27 20:30:35 +00:00
}
}
}
2020-03-01 10:25:50 +00:00
}
2020-03-14 20:35:34 +00:00
bool ECIESX25519AEADRatchetSession : : CheckExpired ( uint64_t ts )
2020-03-01 10:25:50 +00:00
{
2020-03-14 20:35:34 +00:00
CleanupUnconfirmedLeaseSet ( ts ) ;
2021-01-09 23:59:09 +00:00
return ts > m_LastActivityTimestamp + ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT & & // seconds
ts * 1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT * 1000 ; // milliseconds
2020-03-01 10:25:50 +00:00
}
2021-03-03 20:30:13 +00:00
RouterIncomingRatchetSession : : RouterIncomingRatchetSession ( const i2p : : crypto : : NoiseSymmetricState & initState ) :
ECIESX25519AEADRatchetSession ( & i2p : : context , false )
{
2021-11-27 20:30:35 +00:00
SetLeaseSetUpdateStatus ( eLeaseSetDoNotSend ) ;
2021-03-03 20:30:13 +00:00
SetNoiseState ( initState ) ;
2021-11-27 20:30:35 +00:00
}
2021-03-03 20:30:13 +00:00
bool RouterIncomingRatchetSession : : HandleNextMessage ( const uint8_t * buf , size_t len )
{
if ( ! GetOwner ( ) ) return false ;
2021-06-09 16:49:50 +00:00
m_CurrentNoiseState = GetNoiseState ( ) ;
2021-03-03 20:30:13 +00:00
// we are Bob
2021-06-09 16:49:50 +00:00
m_CurrentNoiseState . MixHash ( buf , 32 ) ;
2021-03-03 20:30:13 +00:00
uint8_t sharedSecret [ 32 ] ;
2021-08-31 22:51:40 +00:00
if ( ! GetOwner ( ) - > Decrypt ( buf , sharedSecret , i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ) ) // x25519(bsk, aepk)
2021-03-03 20:30:13 +00:00
{
LogPrint ( eLogWarning , " Garlic: Incorrect N ephemeral public key " ) ;
return false ;
2021-11-27 20:30:35 +00:00
}
2021-06-09 16:49:50 +00:00
m_CurrentNoiseState . MixKey ( sharedSecret ) ;
2021-11-27 20:30:35 +00:00
buf + = 32 ; len - = 32 ;
2021-03-03 20:30:13 +00:00
uint8_t nonce [ 12 ] ;
CreateNonce ( 0 , nonce ) ;
2021-11-27 20:30:35 +00:00
std : : vector < uint8_t > payload ( len - 16 ) ;
2021-06-09 16:49:50 +00:00
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( buf , len - 16 , m_CurrentNoiseState . m_H , 32 ,
m_CurrentNoiseState . m_CK + 32 , nonce , payload . data ( ) , len - 16 , false ) ) // decrypt
2021-03-03 20:30:13 +00:00
{
LogPrint ( eLogWarning , " Garlic: Payload for router AEAD verification failed " ) ;
return false ;
}
HandlePayload ( payload . data ( ) , len - 16 , nullptr , 0 ) ;
return true ;
2021-11-27 20:30:35 +00:00
}
2021-07-15 22:18:55 +00:00
2021-11-27 20:30:35 +00:00
static size_t CreateGarlicPayload ( std : : shared_ptr < const I2NPMessage > msg , uint8_t * payload ,
2021-07-24 00:28:55 +00:00
bool datetime , size_t optimalSize )
2020-04-07 15:40:18 +00:00
{
2021-07-16 13:44:22 +00:00
size_t len = 0 ;
if ( datetime )
2021-11-27 20:30:35 +00:00
{
2021-07-16 13:44:22 +00:00
// DateTime
2021-11-27 20:30:35 +00:00
payload [ 0 ] = eECIESx25519BlkDateTime ;
2021-07-16 13:44:22 +00:00
htobe16buf ( payload + 1 , 4 ) ;
2021-11-27 20:30:35 +00:00
htobe32buf ( payload + 3 , i2p : : util : : GetSecondsSinceEpoch ( ) ) ;
2021-07-16 13:44:22 +00:00
len = 7 ;
2021-11-27 20:30:35 +00:00
}
2021-07-15 22:18:55 +00:00
// I2NP
payload + = len ;
2021-07-15 22:30:32 +00:00
uint16_t cloveSize = msg - > GetPayloadLength ( ) + 10 ;
2020-03-01 10:25:50 +00:00
payload [ 0 ] = eECIESx25519BlkGalicClove ; // clove type
htobe16buf ( payload + 1 , cloveSize ) ; // size
2020-04-07 15:40:18 +00:00
payload + = 3 ;
2021-07-15 22:30:32 +00:00
payload [ 0 ] = 0 ; // flag and delivery instructions
payload [ 1 ] = msg - > GetTypeID ( ) ; // I2NP msg type
htobe32buf ( payload + 2 , msg - > GetMsgID ( ) ) ; // msgID
htobe32buf ( payload + 6 , msg - > GetExpiration ( ) / 1000 ) ; // expiration in seconds
memcpy ( payload + 10 , msg - > GetPayload ( ) , msg - > GetPayloadLength ( ) ) ;
2021-07-15 22:18:55 +00:00
len + = cloveSize + 3 ;
2021-07-15 22:30:32 +00:00
payload + = cloveSize ;
2021-07-15 22:18:55 +00:00
// padding
2021-07-24 00:28:55 +00:00
int delta = ( int ) optimalSize - ( int ) len ;
if ( delta < 0 | | delta > 3 ) // don't create padding if we are close to optimal size
{
uint8_t paddingSize = rand ( ) & 0x0F ; // 0 - 15
if ( delta > 3 )
{
delta - = 3 ;
if ( paddingSize > delta ) paddingSize % = delta ;
}
2021-11-27 20:30:35 +00:00
payload [ 0 ] = eECIESx25519BlkPadding ;
htobe16buf ( payload + 1 , paddingSize ) ;
2021-07-24 00:28:55 +00:00
if ( paddingSize ) memset ( payload + 3 , 0 , paddingSize ) ;
len + = paddingSize + 3 ;
2021-11-27 20:30:35 +00:00
}
2021-07-15 22:18:55 +00:00
return len ;
2021-11-27 20:30:35 +00:00
}
2024-02-22 00:46:29 +00:00
std : : shared_ptr < I2NPMessage > WrapECIESX25519Message ( std : : shared_ptr < I2NPMessage > msg , const uint8_t * key , uint64_t tag )
2021-07-15 22:18:55 +00:00
{
2023-03-22 01:25:00 +00:00
auto m = NewI2NPMessage ( ( msg ? msg - > GetPayloadLength ( ) : 0 ) + 128 ) ;
2021-07-15 22:18:55 +00:00
m - > Align ( 12 ) ; // in order to get buf aligned to 16 (12 + 4)
uint8_t * buf = m - > GetPayload ( ) + 4 ; // 4 bytes for length
size_t offset = 0 ;
memcpy ( buf + offset , & tag , 8 ) ; offset + = 8 ;
auto payload = buf + offset ;
2021-07-24 00:28:55 +00:00
size_t len = CreateGarlicPayload ( msg , payload , false , 956 ) ; // 1003 - 8 tag - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 3 local tunnel delivery
2021-07-15 22:18:55 +00:00
uint8_t nonce [ 12 ] ;
memset ( nonce , 0 , 12 ) ; // n = 0
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( payload , len , buf , 8 , key , nonce , payload , len + 16 , true ) ) // encrypt
2020-04-07 15:40:18 +00:00
{
LogPrint ( eLogWarning , " Garlic: Payload section AEAD encryption failed " ) ;
return nullptr ;
2020-03-01 10:25:50 +00:00
}
2020-04-07 15:40:18 +00:00
offset + = len + 16 ;
htobe32buf ( m - > GetPayload ( ) , offset ) ;
m - > len + = offset + 4 ;
m - > FillI2NPMessageHeader ( eI2NPGarlic ) ;
2024-02-22 00:46:29 +00:00
if ( msg - > onDrop )
{
// move onDrop to the wrapping I2NP messages
m - > onDrop = msg - > onDrop ;
msg - > onDrop = nullptr ;
}
2020-04-07 15:40:18 +00:00
return m ;
}
2020-03-01 10:25:50 +00:00
2024-01-30 20:41:57 +00:00
std : : shared_ptr < I2NPMessage > WrapECIESX25519MessageForRouter ( std : : shared_ptr < I2NPMessage > msg , const uint8_t * routerPublicKey )
2021-07-10 18:33:23 +00:00
{
2021-07-15 22:18:55 +00:00
// Noise_N, we are Alice, routerPublicKey is Bob's
i2p : : crypto : : NoiseSymmetricState noiseState ;
i2p : : crypto : : InitNoiseNState ( noiseState , routerPublicKey ) ;
2023-03-22 01:25:00 +00:00
auto m = NewI2NPMessage ( ( msg ? msg - > GetPayloadLength ( ) : 0 ) + 128 ) ;
2021-07-15 22:18:55 +00:00
m - > Align ( 12 ) ; // in order to get buf aligned to 16 (12 + 4)
uint8_t * buf = m - > GetPayload ( ) + 4 ; // 4 bytes for length
size_t offset = 0 ;
auto ephemeralKeys = i2p : : transport : : transports . GetNextX25519KeysPair ( ) ;
memcpy ( buf + offset , ephemeralKeys - > GetPublicKey ( ) , 32 ) ;
noiseState . MixHash ( buf + offset , 32 ) ; // h = SHA256(h || aepk)
offset + = 32 ;
uint8_t sharedSecret [ 32 ] ;
if ( ! ephemeralKeys - > Agree ( routerPublicKey , sharedSecret ) ) // x25519(aesk, bpk)
{
LogPrint ( eLogWarning , " Garlic: Incorrect Bob static key " ) ;
return nullptr ;
2021-11-27 20:30:35 +00:00
}
noiseState . MixKey ( sharedSecret ) ;
2021-07-15 22:18:55 +00:00
auto payload = buf + offset ;
2021-07-24 00:28:55 +00:00
size_t len = CreateGarlicPayload ( msg , payload , true , 900 ) ; // 1003 - 32 eph key - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 35 router tunnel delivery
2021-07-15 22:18:55 +00:00
uint8_t nonce [ 12 ] ;
memset ( nonce , 0 , 12 ) ;
// encrypt payload
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( payload , len , noiseState . m_H , 32 , noiseState . m_CK + 32 , nonce , payload , len + 16 , true ) ) // encrypt
{
LogPrint ( eLogWarning , " Garlic: Payload for router AEAD encryption failed " ) ;
return nullptr ;
}
offset + = len + 16 ;
htobe32buf ( m - > GetPayload ( ) , offset ) ;
m - > len + = offset + 4 ;
m - > FillI2NPMessageHeader ( eI2NPGarlic ) ;
2024-01-30 20:41:57 +00:00
if ( msg - > onDrop )
{
// move onDrop to the wrapping I2NP messages
m - > onDrop = msg - > onDrop ;
msg - > onDrop = nullptr ;
}
2021-07-15 22:18:55 +00:00
return m ;
2021-11-27 20:30:35 +00:00
}
2020-01-15 20:13:43 +00:00
}
}