@ -15,36 +15,36 @@ namespace i2p
namespace garlic
{
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]
i2p : : crypto : : HKDF ( keydata + 32 , nullptr , 0 , " TagAndKeyGenKeys " , m_KeyData . buf ) ;
// [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64)
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]
i2p : : crypto : : HKDF ( keydata + 32 , nullptr , 0 , " TagAndKeyGenKeys " , m_KeyData . buf ) ;
// [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64)
memcpy ( m_SymmKeyCK , m_KeyData . buf + 32 , 32 ) ;
m_NextSymmKeyIndex = 0 ;
}
}
void RatchetTagSet : : NextSessionTagRatchet ( )
{
i2p : : crypto : : HKDF ( m_KeyData . GetSessTagCK ( ) , nullptr , 0 , " STInitialization " , m_KeyData . buf ) ; // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64)
memcpy ( m_SessTagConstant , m_KeyData . GetSessTagConstant ( ) , 32 ) ;
void RatchetTagSet : : NextSessionTagRatchet ( )
{
i2p : : crypto : : HKDF ( m_KeyData . GetSessTagCK ( ) , nullptr , 0 , " STInitialization " , m_KeyData . buf ) ; // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64)
memcpy ( m_SessTagConstant , m_KeyData . GetSessTagConstant ( ) , 32 ) ;
m_NextIndex = 0 ;
}
}
uint64_t RatchetTagSet : : GetNextSessionTag ( )
{
i2p : : crypto : : HKDF ( m_KeyData . GetSessTagCK ( ) , m_SessTagConstant , 32 , " SessionTagKeyGen " , m_KeyData . buf ) ; // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
uint64_t RatchetTagSet : : GetNextSessionTag ( )
{
i2p : : crypto : : HKDF ( m_KeyData . GetSessTagCK ( ) , m_SessTagConstant , 32 , " SessionTagKeyGen " , m_KeyData . buf ) ; // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
m_NextIndex + + ;
if ( m_NextIndex > = 65535 )
{
LogPrint ( eLogError , " Garlic: Tagset " , GetTagSetID ( ) , " is empty " ) ;
return 0 ;
}
return m_KeyData . GetTag ( ) ;
}
return m_KeyData . GetTag ( ) ;
}
void RatchetTagSet : : GetSymmKey ( int index , uint8_t * key )
{
@ -85,15 +85,15 @@ namespace garlic
m_ExpirationTimestamp = i2p : : util : : GetSecondsSinceEpoch ( ) + ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT ;
}
ECIESX25519AEADRatchetSession : : ECIESX25519AEADRatchetSession ( GarlicDestination * owner , bool attachLeaseSet ) :
GarlicRoutingSession ( owner , attachLeaseSet )
{
ResetKeys ( ) ;
}
ECIESX25519AEADRatchetSession : : ECIESX25519AEADRatchetSession ( GarlicDestination * owner , bool attachLeaseSet ) :
GarlicRoutingSession ( owner , attachLeaseSet )
{
ResetKeys ( ) ;
}
ECIESX25519AEADRatchetSession : : ~ ECIESX25519AEADRatchetSession ( )
{
}
ECIESX25519AEADRatchetSession : : ~ ECIESX25519AEADRatchetSession ( )
{
}
void ECIESX25519AEADRatchetSession : : ResetKeys ( )
{
@ -111,14 +111,14 @@ namespace garlic
memcpy ( m_H , hh , 32 ) ;
}
void ECIESX25519AEADRatchetSession : : MixHash ( const uint8_t * buf , size_t len )
{
SHA256_CTX ctx ;
void ECIESX25519AEADRatchetSession : : MixHash ( const uint8_t * buf , size_t len )
{
SHA256_CTX ctx ;
SHA256_Init ( & ctx ) ;
SHA256_Update ( & ctx , m_H , 32 ) ;
SHA256_Update ( & ctx , buf , len ) ;
SHA256_Final ( m_H , & ctx ) ;
}
}
void ECIESX25519AEADRatchetSession : : CreateNonce ( uint64_t seqn , uint8_t * nonce )
{
@ -126,48 +126,48 @@ namespace garlic
htole64buf ( nonce + 4 , seqn ) ;
}
bool ECIESX25519AEADRatchetSession : : GenerateEphemeralKeysAndEncode ( uint8_t * buf )
{
for ( int i = 0 ; i < 10 ; i + + )
{
m_EphemeralKeys . GenerateKeys ( ) ;
if ( i2p : : crypto : : GetElligator ( ) - > Encode ( m_EphemeralKeys . GetPublicKey ( ) , buf ) )
return true ; // success
}
return false ;
}
std : : shared_ptr < RatchetTagSet > ECIESX25519AEADRatchetSession : : CreateNewSessionTagset ( )
{
uint8_t tagsetKey [ 32 ] ;
i2p : : crypto : : HKDF ( m_CK , nullptr , 0 , " SessionReplyTags " , tagsetKey , 32 ) ; // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32)
// Session Tag Ratchet
auto tagsetNsr = std : : make_shared < RatchetTagSet > ( shared_from_this ( ) ) ;
tagsetNsr - > DHInitialize ( m_CK , tagsetKey ) ; // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey)
tagsetNsr - > NextSessionTagRatchet ( ) ;
return tagsetNsr ;
}
bool ECIESX25519AEADRatchetSession : : HandleNewIncomingSession ( const uint8_t * buf , size_t len )
{
if ( ! GetOwner ( ) ) return false ;
// we are Bob
// KDF1
MixHash ( GetOwner ( ) - > GetEncryptionPublicKey ( i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ) , 32 ) ; // h = SHA256(h || bpk)
bool ECIESX25519AEADRatchetSession : : GenerateEphemeralKeysAndEncode ( uint8_t * buf )
{
for ( int i = 0 ; i < 10 ; i + + )
{
m_EphemeralKeys . GenerateKeys ( ) ;
if ( i2p : : crypto : : GetElligator ( ) - > Encode ( m_EphemeralKeys . GetPublicKey ( ) , buf ) )
return true ; // success
}
return false ;
}
std : : shared_ptr < RatchetTagSet > ECIESX25519AEADRatchetSession : : CreateNewSessionTagset ( )
{
uint8_t tagsetKey [ 32 ] ;
i2p : : crypto : : HKDF ( m_CK , nullptr , 0 , " SessionReplyTags " , tagsetKey , 32 ) ; // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32)
// Session Tag Ratchet
auto tagsetNsr = std : : make_shared < RatchetTagSet > ( shared_from_this ( ) ) ;
tagsetNsr - > DHInitialize ( m_CK , tagsetKey ) ; // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey)
tagsetNsr - > NextSessionTagRatchet ( ) ;
return tagsetNsr ;
}
bool ECIESX25519AEADRatchetSession : : HandleNewIncomingSession ( const uint8_t * buf , size_t len )
{
if ( ! GetOwner ( ) ) return false ;
// we are Bob
// KDF1
MixHash ( GetOwner ( ) - > GetEncryptionPublicKey ( i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ) , 32 ) ; // h = SHA256(h || bpk)
if ( ! i2p : : crypto : : GetElligator ( ) - > Decode ( buf , m_Aepk ) )
{
LogPrint ( eLogError , " Garlic: Can't decode elligator " ) ;
return false ;
}
buf + = 32 ; len - = 32 ;
MixHash ( m_Aepk , 32 ) ; // h = SHA256(h || aepk)
buf + = 32 ; len - = 32 ;
MixHash ( m_Aepk , 32 ) ; // h = SHA256(h || aepk)
uint8_t sharedSecret [ 32 ] ;
uint8_t sharedSecret [ 32 ] ;
GetOwner ( ) - > Decrypt ( m_Aepk , sharedSecret , nullptr , i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ) ; // x25519(bsk, aepk)
i2p : : crypto : : HKDF ( m_CK , sharedSecret , 32 , " " , m_CK ) ; // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
// decrypt flags/static
// decrypt flags/static
uint8_t nonce [ 12 ] , fs [ 32 ] ;
CreateNonce ( 0 , nonce ) ;
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( buf , 32 , m_H , 32 , m_CK + 32 , nonce , fs , 32 , false ) ) // decrypt
@ -183,7 +183,7 @@ namespace garlic
if ( isStatic )
{
// static key, fs is apk
memcpy ( m_RemoteStaticKey , fs , 32 ) ;
memcpy ( m_RemoteStaticKey , fs , 32 ) ;
GetOwner ( ) - > Decrypt ( fs , sharedSecret , nullptr , i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ) ; // x25519(bsk, apk)
i2p : : crypto : : HKDF ( m_CK , sharedSecret , 32 , " " , m_CK ) ; // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
}
@ -198,17 +198,17 @@ namespace garlic
return false ;
}
if ( isStatic ) MixHash ( buf , len ) ; // h = SHA256(h || ciphertext)
m_State = eSessionStateNewSessionReceived ;
m_State = eSessionStateNewSessionReceived ;
GetOwner ( ) - > AddECIESx25519Session ( m_RemoteStaticKey , shared_from_this ( ) ) ;
HandlePayload ( payload . data ( ) , len - 16 , nullptr , 0 ) ;
HandlePayload ( payload . data ( ) , len - 16 , nullptr , 0 ) ;
return true ;
}
return true ;
}
void ECIESX25519AEADRatchetSession : : HandlePayload ( const uint8_t * buf , size_t len , const std : : shared_ptr < RatchetTagSet > & receiveTagset , int index )
{
size_t offset = 0 ;
void ECIESX25519AEADRatchetSession : : HandlePayload ( const uint8_t * buf , size_t len , const std : : shared_ptr < RatchetTagSet > & receiveTagset , int index )
{
size_t offset = 0 ;
while ( offset < len )
{
uint8_t blk = buf [ offset ] ;
@ -269,7 +269,7 @@ namespace garlic
}
offset + = size ;
}
}
}
void ECIESX25519AEADRatchetSession : : HandleNextKey ( const uint8_t * buf , size_t len , const std : : shared_ptr < RatchetTagSet > & receiveTagset )
{
@ -279,7 +279,7 @@ namespace garlic
if ( ! m_SendForwardKey | | ! m_NextSendRatchet ) return ;
uint16_t keyID = bufbe16toh ( buf ) ; buf + = 2 ; // keyID
if ( ( ( ! m_NextSendRatchet - > newKey | | ! m_NextSendRatchet - > keyID ) & & keyID = = m_NextSendRatchet - > keyID ) | |
( m_NextSendRatchet - > newKey & & keyID = = m_NextSendRatchet - > keyID - 1 ) )
( m_NextSendRatchet - > newKey & & keyID = = m_NextSendRatchet - > keyID - 1 ) )
{
if ( flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG )
memcpy ( m_NextSendRatchet - > remote , buf , 32 ) ;
@ -359,36 +359,36 @@ namespace garlic
LogPrint ( eLogDebug , " Garlic: new send ratchet " , m_NextSendRatchet - > newKey ? " new " : " old " , " key " , m_NextSendRatchet - > keyID , " created " ) ;
}
bool ECIESX25519AEADRatchetSession : : NewOutgoingSessionMessage ( const uint8_t * payload , size_t len , uint8_t * out , size_t outLen )
{
bool ECIESX25519AEADRatchetSession : : NewOutgoingSessionMessage ( const uint8_t * payload , size_t len , uint8_t * out , size_t outLen )
{
ResetKeys ( ) ;
// we are Alice, bpk is m_RemoteStaticKey
size_t offset = 0 ;
if ( ! GenerateEphemeralKeysAndEncode ( out + offset ) )
// we are Alice, bpk is m_RemoteStaticKey
size_t offset = 0 ;
if ( ! GenerateEphemeralKeysAndEncode ( out + offset ) )
{
LogPrint ( eLogError , " Garlic: Can't encode elligator " ) ;
return false ;
}
offset + = 32 ;
offset + = 32 ;
// KDF1
MixHash ( m_RemoteStaticKey , 32 ) ; // h = SHA256(h || bpk)
MixHash ( m_EphemeralKeys . GetPublicKey ( ) , 32 ) ; // h = SHA256(h || aepk)
uint8_t sharedSecret [ 32 ] ;
// KDF1
MixHash ( m_RemoteStaticKey , 32 ) ; // h = SHA256(h || bpk)
MixHash ( m_EphemeralKeys . GetPublicKey ( ) , 32 ) ; // h = SHA256(h || aepk)
uint8_t sharedSecret [ 32 ] ;
m_EphemeralKeys . Agree ( m_RemoteStaticKey , sharedSecret ) ; // x25519(aesk, bpk)
i2p : : crypto : : HKDF ( m_CK , sharedSecret , 32 , " " , m_CK ) ; // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
// encrypt static key section
uint8_t nonce [ 12 ] ;
// encrypt static key section
uint8_t nonce [ 12 ] ;
CreateNonce ( 0 , nonce ) ;
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( GetOwner ( ) - > GetEncryptionPublicKey ( i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ) , 32 , m_H , 32 , m_CK + 32 , nonce , out + offset , 48 , true ) ) // encrypt
{
LogPrint ( eLogWarning , " Garlic: Static section AEAD encryption failed " ) ;
return false ;
}
MixHash ( out + offset , 48 ) ; // h = SHA256(h || ciphertext)
offset + = 48 ;
// KDF2
GetOwner ( ) - > Decrypt ( m_RemoteStaticKey , sharedSecret , nullptr , i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ) ; // x25519 (ask, bpk)
MixHash ( out + offset , 48 ) ; // h = SHA256(h || ciphertext)
offset + = 48 ;
// KDF2
GetOwner ( ) - > Decrypt ( m_RemoteStaticKey , sharedSecret , nullptr , i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ) ; // x25519 (ask, bpk)
i2p : : crypto : : HKDF ( m_CK , sharedSecret , 32 , " " , m_CK ) ; // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
// encrypt payload
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( payload , len , m_H , 32 , m_CK + 32 , nonce , out + offset , len + 16 , true ) ) // encrypt
@ -399,80 +399,80 @@ namespace garlic
MixHash ( out + offset , len + 16 ) ; // h = SHA256(h || ciphertext)
m_State = eSessionStateNewSessionSent ;
if ( GetOwner ( ) )
if ( GetOwner ( ) )
GenerateMoreReceiveTags ( CreateNewSessionTagset ( ) , ECIESX25519_NSR_NUM_GENERATED_TAGS ) ;
return true ;
}
return true ;
}
bool ECIESX25519AEADRatchetSession : : NewSessionReplyMessage ( const uint8_t * payload , size_t len , uint8_t * out , size_t outLen )
{
// we are Bob
bool ECIESX25519AEADRatchetSession : : NewSessionReplyMessage ( const uint8_t * payload , size_t len , uint8_t * out , size_t outLen )
{
// we are Bob
m_NSRTagset = CreateNewSessionTagset ( ) ;
uint64_t tag = m_NSRTagset - > GetNextSessionTag ( ) ;
uint64_t tag = m_NSRTagset - > GetNextSessionTag ( ) ;
size_t offset = 0 ;
memcpy ( out + offset , & tag , 8 ) ;
offset + = 8 ;
if ( ! GenerateEphemeralKeysAndEncode ( out + offset ) ) // bepk
size_t offset = 0 ;
memcpy ( out + offset , & tag , 8 ) ;
offset + = 8 ;
if ( ! GenerateEphemeralKeysAndEncode ( out + offset ) ) // bepk
{
LogPrint ( eLogError , " Garlic: Can't encode elligator " ) ;
return false ;
}
memcpy ( m_NSREncodedKey , out + offset , 56 ) ; // for possible next NSR
memcpy ( m_NSRH , m_H , 32 ) ;
offset + = 32 ;
// KDF for Reply Key Section
MixHash ( ( const uint8_t * ) & tag , 8 ) ; // h = SHA256(h || tag)
MixHash ( m_EphemeralKeys . GetPublicKey ( ) , 32 ) ; // h = SHA256(h || bepk)
uint8_t sharedSecret [ 32 ] ;
m_EphemeralKeys . Agree ( m_Aepk , sharedSecret ) ; // sharedSecret = x25519(besk, aepk)
i2p : : crypto : : HKDF ( m_CK , sharedSecret , 32 , " " , m_CK , 32 ) ; // chainKey = HKDF(chainKey, sharedSecret, "", 32)
offset + = 32 ;
// KDF for Reply Key Section
MixHash ( ( const uint8_t * ) & tag , 8 ) ; // h = SHA256(h || tag)
MixHash ( m_EphemeralKeys . GetPublicKey ( ) , 32 ) ; // h = SHA256(h || bepk)
uint8_t sharedSecret [ 32 ] ;
m_EphemeralKeys . Agree ( m_Aepk , sharedSecret ) ; // sharedSecret = x25519(besk, aepk)
i2p : : crypto : : HKDF ( m_CK , sharedSecret , 32 , " " , m_CK , 32 ) ; // chainKey = HKDF(chainKey, sharedSecret, "", 32)
m_EphemeralKeys . Agree ( m_RemoteStaticKey , sharedSecret ) ; // sharedSecret = x25519(besk, apk)
i2p : : crypto : : HKDF ( m_CK , sharedSecret , 32 , " " , m_CK ) ; // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
i2p : : crypto : : HKDF ( m_CK , sharedSecret , 32 , " " , m_CK ) ; // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
uint8_t nonce [ 12 ] ;
CreateNonce ( 0 , nonce ) ;
// cal ulate hash for zero length
// calc ulate hash for zero length
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)
{
LogPrint ( eLogWarning , " Garlic: Reply key section AEAD encryption failed " ) ;
return false ;
}
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)
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)
// k_ab = keydata[0:31], k_ba = keydata[32:63]
auto receiveTagset = std : : make_shared < RatchetTagSet > ( shared_from_this ( ) ) ;
receiveTagset - > DHInitialize ( m_CK , keydata ) ; // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
receiveTagset - > DHInitialize ( m_CK , keydata ) ; // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
receiveTagset - > NextSessionTagRatchet ( ) ;
m_SendTagset = std : : make_shared < RatchetTagSet > ( shared_from_this ( ) ) ;
m_SendTagset - > DHInitialize ( m_CK , keydata + 32 ) ; // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
m_SendTagset - > DHInitialize ( m_CK , keydata + 32 ) ; // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
m_SendTagset - > NextSessionTagRatchet ( ) ;
GenerateMoreReceiveTags ( receiveTagset , ECIESX25519_MIN_NUM_GENERATED_TAGS ) ;
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
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
{
LogPrint ( eLogWarning , " Garlic: NSR payload section AEAD encryption failed " ) ;
return false ;
}
m_State = eSessionStateNewSessionReplySent ;
return true ;
}
return true ;
}
bool ECIESX25519AEADRatchetSession : : NextNewSessionReplyMessage ( const uint8_t * payload , size_t len , uint8_t * out , size_t outLen )
{
// we are Bob and sent NSR already
uint64_t tag = m_NSRTagset - > GetNextSessionTag ( ) ; // next tag
memcpy ( out , & tag , 8 ) ;
{
// we are Bob and sent NSR already
uint64_t tag = m_NSRTagset - > GetNextSessionTag ( ) ; // next tag
memcpy ( out , & tag , 8 ) ;
memcpy ( out + 8 , m_NSREncodedKey , 32 ) ;
// recalcul te h with new tag
// recalcul a te h with new tag
memcpy ( m_H , m_NSRH , 32 ) ;
MixHash ( ( const uint8_t * ) & tag , 8 ) ; // h = SHA256(h || tag)
MixHash ( m_EphemeralKeys . GetPublicKey ( ) , 32 ) ; // h = SHA256(h || bepk)
MixHash ( m_EphemeralKeys . GetPublicKey ( ) , 32 ) ; // h = SHA256(h || bepk)
uint8_t nonce [ 12 ] ;
CreateNonce ( 0 , nonce ) ;
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)
@ -482,7 +482,7 @@ namespace garlic
}
MixHash ( out + 40 , 16 ) ; // h = SHA256(h || ciphertext)
// encrypt payload
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( payload , len , m_H , 32 , m_NSRKey , nonce , out + 56 , len + 16 , true ) ) // encrypt
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( payload , len , m_H , 32 , m_NSRKey , nonce , out + 56 , len + 16 , true ) ) // encrypt
{
LogPrint ( eLogWarning , " Garlic: Next NSR payload section AEAD encryption failed " ) ;
return false ;
@ -490,36 +490,36 @@ namespace garlic
return true ;
}
bool ECIESX25519AEADRatchetSession : : HandleNewOutgoingSessionReply ( uint8_t * buf , size_t len )
{
bool ECIESX25519AEADRatchetSession : : HandleNewOutgoingSessionReply ( uint8_t * buf , size_t len )
{
// we are Alice
LogPrint ( eLogDebug , " Garlic: reply received " ) ;
const uint8_t * tag = buf ;
buf + = 8 ; len - = 8 ; // tag
uint8_t bepk [ 32 ] ; // Bob's ephemeral key
uint8_t bepk [ 32 ] ; // Bob's ephemeral key
if ( ! i2p : : crypto : : GetElligator ( ) - > Decode ( buf , bepk ) )
{
LogPrint ( eLogError , " Garlic: Can't decode elligator " ) ;
return false ;
}
buf + = 32 ; len - = 32 ;
// KDF for Reply Key Section
// KDF for Reply Key Section
uint8_t h [ 32 ] ; memcpy ( h , m_H , 32 ) ; // save m_H
MixHash ( tag , 8 ) ; // h = SHA256(h || tag)
MixHash ( bepk , 32 ) ; // h = SHA256(h || bepk)
MixHash ( tag , 8 ) ; // h = SHA256(h || tag)
MixHash ( bepk , 32 ) ; // h = SHA256(h || bepk)
uint8_t sharedSecret [ 32 ] ;
if ( m_State = = eSessionStateNewSessionSent )
{
// only fist time, we assume ephemeral keys the same
m_EphemeralKeys . Agree ( bepk , sharedSecret ) ; // sharedSecret = x25519(aesk, bepk)
m_EphemeralKeys . Agree ( bepk , sharedSecret ) ; // sharedSecret = x25519(aesk, bepk)
i2p : : crypto : : HKDF ( m_CK , sharedSecret , 32 , " " , m_CK , 32 ) ; // chainKey = HKDF(chainKey, sharedSecret, "", 32)
GetOwner ( ) - > Decrypt ( bepk , sharedSecret , nullptr , i2p : : data : : CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ) ; // x25519 (ask, bepk)
i2p : : crypto : : HKDF ( m_CK , sharedSecret , 32 , " " , m_CK ) ; // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
}
uint8_t nonce [ 12 ] ;
CreateNonce ( 0 , nonce ) ;
// cal ulate hash for zero length
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( buf , 0 , m_H , 32 , m_CK + 32 , nonce , sharedSecret /* can be anyt ing */, 0 , false ) ) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only
// calc ulate hash for zero length
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( buf , 0 , m_H , 32 , m_CK + 32 , nonce , sharedSecret /* can be anyt h ing */, 0 , false ) ) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only
{
LogPrint ( eLogWarning , " Garlic: Reply key section AEAD decryption failed " ) ;
return false ;
@ -527,22 +527,22 @@ namespace garlic
MixHash ( buf , 16 ) ; // h = SHA256(h || ciphertext)
buf + = 16 ; len - = 16 ;
// KDF for payload
uint8_t keydata [ 64 ] ;
i2p : : crypto : : HKDF ( m_CK , nullptr , 0 , " " , keydata ) ; // keydata = HKDF(chainKey, ZEROLEN, "", 64)
uint8_t keydata [ 64 ] ;
i2p : : crypto : : HKDF ( m_CK , nullptr , 0 , " " , keydata ) ; // keydata = HKDF(chainKey, ZEROLEN, "", 64)
if ( m_State = = eSessionStateNewSessionSent )
{
// k_ab = keydata[0:31], k_ba = keydata[32:63]
// k_ab = keydata[0:31], k_ba = keydata[32:63]
m_SendTagset = std : : make_shared < RatchetTagSet > ( shared_from_this ( ) ) ;
m_SendTagset - > DHInitialize ( m_CK , keydata ) ; // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
m_SendTagset - > DHInitialize ( m_CK , keydata ) ; // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
m_SendTagset - > NextSessionTagRatchet ( ) ;
auto receiveTagset = std : : make_shared < RatchetTagSet > ( shared_from_this ( ) ) ;
receiveTagset - > DHInitialize ( m_CK , keydata + 32 ) ; // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
receiveTagset - > DHInitialize ( m_CK , keydata + 32 ) ; // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
receiveTagset - > NextSessionTagRatchet ( ) ;
GenerateMoreReceiveTags ( receiveTagset , ECIESX25519_MIN_NUM_GENERATED_TAGS ) ;
}
i2p : : crypto : : HKDF ( keydata + 32 , nullptr , 0 , " AttachPayloadKDF " , keydata , 32 ) ; // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
i2p : : crypto : : HKDF ( keydata + 32 , nullptr , 0 , " AttachPayloadKDF " , keydata , 32 ) ; // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
// decrypt payload
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( buf , len - 16 , m_H , 32 , keydata , nonce , buf , len - 16 , false ) ) // decrypt
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( buf , len - 16 , m_H , 32 , keydata , nonce , buf , len - 16 , false ) ) // decrypt
{
LogPrint ( eLogWarning , " Garlic: Payload section AEAD decryption failed " ) ;
return false ;
@ -560,8 +560,8 @@ namespace garlic
SetLeaseSetUpdateStatus ( eLeaseSetUpToDate ) ;
SetLeaseSetUpdateMsgID ( 0 ) ;
return true ;
}
return true ;
}
bool ECIESX25519AEADRatchetSession : : NewExistingSessionMessage ( const uint8_t * payload , size_t len , uint8_t * out , size_t outLen )
{
@ -637,61 +637,61 @@ namespace garlic
return true ;
}
std : : shared_ptr < I2NPMessage > ECIESX25519AEADRatchetSession : : WrapSingleMessage ( std : : shared_ptr < const I2NPMessage > msg )
{
auto payload = CreatePayload ( msg , m_State ! = eSessionStateEstablished ) ;
size_t len = payload . size ( ) ;
std : : shared_ptr < I2NPMessage > ECIESX25519AEADRatchetSession : : WrapSingleMessage ( std : : shared_ptr < const I2NPMessage > msg )
{
auto payload = CreatePayload ( msg , m_State ! = eSessionStateEstablished ) ;
size_t len = payload . size ( ) ;
if ( ! len ) return nullptr ;
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
switch ( m_State )
{
switch ( m_State )
{
case eSessionStateEstablished :
if ( ! NewExistingSessionMessage ( payload . data ( ) , payload . size ( ) , buf , m - > maxLen ) )
return nullptr ;
len + = 24 ;
break ;
case eSessionStateNew :
if ( ! NewOutgoingSessionMessage ( payload . data ( ) , payload . size ( ) , buf , m - > maxLen ) )
return nullptr ;
len + = 96 ;
break ;
case eSessionStateNewSessionReceived :
if ( ! NewSessionReplyMessage ( payload . data ( ) , payload . size ( ) , buf , m - > maxLen ) )
return nullptr ;
len + = 72 ;
break ;
case eSessionStateNew :
if ( ! NewOutgoingSessionMessage ( payload . data ( ) , payload . size ( ) , buf , m - > maxLen ) )
return nullptr ;
len + = 96 ;
break ;
case eSessionStateNewSessionReceived :
if ( ! NewSessionReplyMessage ( payload . data ( ) , payload . size ( ) , buf , m - > maxLen ) )
return nullptr ;
len + = 72 ;
break ;
case eSessionStateNewSessionReplySent :
if ( ! NextNewSessionReplyMessage ( payload . data ( ) , payload . size ( ) , buf , m - > maxLen ) )
return nullptr ;
len + = 72 ;
return nullptr ;
len + = 72 ;
break ;
default :
return nullptr ;
}
default :
return nullptr ;
}
htobe32buf ( m - > GetPayload ( ) , len ) ;
htobe32buf ( m - > GetPayload ( ) , len ) ;
m - > len + = len + 4 ;
m - > FillI2NPMessageHeader ( eI2NPGarlic ) ;
return m ;
}
}
std : : vector < uint8_t > ECIESX25519AEADRatchetSession : : CreatePayload ( std : : shared_ptr < const I2NPMessage > msg , bool first )
{
std : : vector < uint8_t > ECIESX25519AEADRatchetSession : : CreatePayload ( std : : shared_ptr < const I2NPMessage > msg , bool first )
{
uint64_t ts = i2p : : util : : GetMillisecondsSinceEpoch ( ) ;
size_t payloadLen = 0 ;
size_t payloadLen = 0 ;
if ( first ) payloadLen + = 7 ; // datatime
if ( msg & & m_Destination )
payloadLen + = msg - > GetPayloadLength ( ) + 13 + 32 ;
if ( msg & & m_Destination )
payloadLen + = msg - > GetPayloadLength ( ) + 13 + 32 ;
auto leaseSet = ( GetLeaseSetUpdateStatus ( ) = = eLeaseSetUpdated | |
( GetLeaseSetUpdateStatus ( ) = = eLeaseSetSubmitted & &
ts > GetLeaseSetSubmissionTime ( ) + LEASET_CONFIRMATION_TIMEOUT ) ) ?
ts > GetLeaseSetSubmissionTime ( ) + LEASET_CONFIRMATION_TIMEOUT ) ) ?
GetOwner ( ) - > GetLeaseSet ( ) : nullptr ;
if ( leaseSet )
{
payloadLen + = leaseSet - > GetBufferLen ( ) + DATABASE_STORE_HEADER_SIZE + 13 ;
payloadLen + = leaseSet - > GetBufferLen ( ) + DATABASE_STORE_HEADER_SIZE + 13 ;
if ( ! first )
{
// ack request
@ -730,19 +730,19 @@ namespace garlic
payloadLen + = paddingSize + 3 ;
}
}
std : : vector < uint8_t > v ( payloadLen ) ;
size_t offset = 0 ;
// DateTime
std : : vector < uint8_t > v ( payloadLen ) ;
size_t offset = 0 ;
// DateTime
if ( first )
{
v [ offset ] = eECIESx25519BlkDateTime ; offset + + ;
htobe16buf ( v . data ( ) + offset , 4 ) ; offset + = 2 ;
htobe32buf ( v . data ( ) + offset , ts / 1000 ) ; offset + = 4 ; // in seconds
v [ offset ] = eECIESx25519BlkDateTime ; offset + + ;
htobe16buf ( v . data ( ) + offset , 4 ) ; offset + = 2 ;
htobe32buf ( v . data ( ) + offset , ts / 1000 ) ; offset + = 4 ; // in seconds
}
// LeaseSet
if ( leaseSet )
// LeaseSet
if ( leaseSet )
{
offset + = CreateLeaseSetClove ( leaseSet , ts , v . data ( ) + offset , payloadLen - offset ) ;
offset + = CreateLeaseSetClove ( leaseSet , ts , v . data ( ) + offset , payloadLen - offset ) ;
if ( ! first )
{
// ack request
@ -752,13 +752,13 @@ namespace garlic
}
}
// msg
if ( msg & & m_Destination )
offset + = CreateGarlicClove ( msg , v . data ( ) + offset , payloadLen - offset , true ) ;
if ( msg & & m_Destination )
offset + = CreateGarlicClove ( msg , v . data ( ) + offset , payloadLen - offset , true ) ;
// ack
if ( m_AckRequests . size ( ) > 0 )
{
v [ offset ] = eECIESx25519BlkAck ; offset + + ;
htobe16buf ( v . data ( ) + offset , m_AckRequests . size ( ) * 4 ) ; offset + = 2 ;
htobe16buf ( v . data ( ) + offset , m_AckRequests . size ( ) * 4 ) ; offset + = 2 ;
for ( auto & it : m_AckRequests )
{
htobe16buf ( v . data ( ) + offset , it . first ) ; offset + = 2 ;
@ -801,24 +801,24 @@ namespace garlic
offset + = 32 ; // public key
}
}
// padding
// padding
if ( paddingSize )
{
v [ offset ] = eECIESx25519BlkPadding ; offset + + ;
htobe16buf ( v . data ( ) + offset , paddingSize ) ; offset + = 2 ;
memset ( v . data ( ) + offset , 0 , paddingSize ) ; offset + = paddingSize ;
v [ offset ] = eECIESx25519BlkPadding ; offset + + ;
htobe16buf ( v . data ( ) + offset , paddingSize ) ; offset + = 2 ;
memset ( v . data ( ) + offset , 0 , paddingSize ) ; offset + = paddingSize ;
}
return v ;
}
return v ;
}
size_t ECIESX25519AEADRatchetSession : : CreateGarlicClove ( std : : shared_ptr < const I2NPMessage > msg , uint8_t * buf , size_t len , bool isDestination )
{
if ( ! msg ) return 0 ;
uint16_t cloveSize = msg - > GetPayloadLength ( ) + 9 + 1 ;
size_t ECIESX25519AEADRatchetSession : : CreateGarlicClove ( std : : shared_ptr < const I2NPMessage > msg , uint8_t * buf , size_t len , bool isDestination )
{
if ( ! msg ) return 0 ;
uint16_t cloveSize = msg - > GetPayloadLength ( ) + 9 + 1 ;
if ( isDestination ) cloveSize + = 32 ;
if ( ( int ) len < cloveSize + 3 ) return 0 ;
buf [ 0 ] = eECIESx25519BlkGalicClove ; // clove type
htobe16buf ( buf + 1 , cloveSize ) ; // size
if ( ( int ) len < cloveSize + 3 ) return 0 ;
buf [ 0 ] = eECIESx25519BlkGalicClove ; // clove type
htobe16buf ( buf + 1 , cloveSize ) ; // size
buf + = 3 ;
if ( isDestination )
{
@ -828,15 +828,15 @@ namespace garlic
else
* buf = 0 ;
buf + + ; // flag and delivery instructions
* 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 ;
}
* 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 ;
}
size_t ECIESX25519AEADRatchetSession : : CreateLeaseSetClove ( std : : shared_ptr < const i2p : : data : : LocalLeaseSet > ls , uint64_t ts , uint8_t * buf , size_t len )
{
{
if ( ! ls | | ls - > GetStoreType ( ) ! = i2p : : data : : NETDB_STORE_TYPE_STANDARD_LEASESET2 )
{
LogPrint ( eLogError , " Garlic: Incorrect LeasetSet type to send " ) ;
@ -845,7 +845,7 @@ namespace garlic
uint16_t cloveSize = 1 + 9 + DATABASE_STORE_HEADER_SIZE + ls - > GetBufferLen ( ) ; // to local
if ( ( int ) len < cloveSize + 3 ) return 0 ;
buf [ 0 ] = eECIESx25519BlkGalicClove ; // clove type
htobe16buf ( buf + 1 , cloveSize ) ; // size
htobe16buf ( buf + 1 , cloveSize ) ; // size
buf + = 3 ;
* buf = 0 ; buf + + ; // flag and delivery instructions
* buf = eI2NPDatabaseStore ; buf + + ; // I2NP msg type
@ -885,14 +885,14 @@ namespace garlic
auto payload = buf + offset ;
uint16_t cloveSize = msg - > GetPayloadLength ( ) + 9 + 1 ;
size_t len = cloveSize + 3 ;
payload [ 0 ] = eECIESx25519BlkGalicClove ; // clove type
htobe16buf ( payload + 1 , cloveSize ) ; // size
payload [ 0 ] = eECIESx25519BlkGalicClove ; // clove type
htobe16buf ( payload + 1 , cloveSize ) ; // size
payload + = 3 ;
* payload = 0 ; payload + + ; // flag and delivery instructions
* payload = msg - > GetTypeID ( ) ; // I2NP msg type
htobe32buf ( payload + 1 , msg - > GetMsgID ( ) ) ; // msgID
htobe32buf ( payload + 5 , msg - > GetExpiration ( ) / 1000 ) ; // expiration in seconds
memcpy ( payload + 9 , msg - > GetPayload ( ) , msg - > GetPayloadLength ( ) ) ;
* payload = 0 ; payload + + ; // flag and delivery instructions
* payload = msg - > GetTypeID ( ) ; // I2NP msg type
htobe32buf ( payload + 1 , msg - > GetMsgID ( ) ) ; // msgID
htobe32buf ( payload + 5 , msg - > GetExpiration ( ) / 1000 ) ; // expiration in seconds
memcpy ( payload + 9 , msg - > GetPayload ( ) , msg - > GetPayloadLength ( ) ) ;
if ( ! i2p : : crypto : : AEADChaCha20Poly1305 ( buf + offset , len , buf , 8 , key , nonce , buf + offset , len + 16 , true ) ) // encrypt
{
@ -909,5 +909,3 @@ namespace garlic
}
}