diff --git a/libi2pd/ECIESX25519AEADRatchetSession.cpp b/libi2pd/ECIESX25519AEADRatchetSession.cpp index 96c917cd..94ccbce5 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.cpp +++ b/libi2pd/ECIESX25519AEADRatchetSession.cpp @@ -27,11 +27,13 @@ namespace garlic { 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) + m_NextIndex++; return m_KeyData.GetTag (); } @@ -64,6 +66,12 @@ namespace garlic SHA256_Final (m_H, &ctx); } + void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce) + { + memset (nonce, 0, 4); + htole64buf (nonce + 4, seqn); + } + bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf) { for (int i = 0; i < 10; i++) @@ -107,7 +115,7 @@ namespace garlic // decrypt flags/static uint8_t nonce[12], fs[32]; - memset (nonce, 0, 12); // n = 0 + CreateNonce (0, nonce); if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt { LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed "); @@ -128,7 +136,7 @@ namespace garlic i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) } else // all zeros flags - htole64buf (nonce + 4, 1); // n = 1 + CreateNonce (1, nonce); if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt { LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed"); @@ -199,7 +207,7 @@ namespace garlic i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) // encrypt static key section uint8_t nonce[12]; - memset (nonce, 0, 12); // n = 0 + CreateNonce (0, nonce); if (!i2p::crypto::AEADChaCha20Poly1305 (GetOwner ()->GetEncryptionPublicKey (), 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt { LogPrint (eLogWarning, "Garlic: Static section AEAD encryption failed "); @@ -220,7 +228,7 @@ namespace garlic m_State = eSessionStateNewSessionSent; if (GetOwner ()) - GetOwner ()->AddECIESx25519SessionTag (CreateNewSessionTag (), 0, shared_from_this ()); + GetOwner ()->AddECIESx25519SessionTag (0, CreateNewSessionTag (), shared_from_this ()); return true; } @@ -248,7 +256,7 @@ namespace garlic 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) uint8_t nonce[12]; - memset (nonce, 0, 12); // n = 0 + CreateNonce (0, nonce); // calulate hash for zero length if (!i2p::crypto::AEADChaCha20Poly1305 (sharedSecret /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) { @@ -266,6 +274,9 @@ namespace garlic m_ReceiveTagset.NextSessionTagRatchet (); m_SendTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) m_SendTagset.NextSessionTagRatchet (); + auto numTags = GetOwner ()->GetNumTags (); + for (int i = 0; i < numTags; i++) + GetOwner ()->AddECIESx25519SessionTag (m_ReceiveTagset.GetNextIndex (), m_ReceiveTagset.GetNextSessionTag (), shared_from_this ()); i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) // encrypt payload if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, keydata, nonce, out + offset, len + 16, true)) // encrypt @@ -300,7 +311,7 @@ namespace garlic GetOwner ()->Decrypt (bepk, sharedSecret, nullptr); // x25519 (ask, bepk) i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) uint8_t nonce[12]; - memset (nonce, 0, 12); // n = 0 + CreateNonce (0, nonce); // calulate hash for zero length if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anyting */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only { @@ -318,6 +329,9 @@ namespace garlic m_SendTagset.NextSessionTagRatchet (); m_ReceiveTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) m_ReceiveTagset.NextSessionTagRatchet (); + auto numTags = GetOwner ()->GetNumTags (); + for (int i = 0; i < numTags; i++) + GetOwner ()->AddECIESx25519SessionTag (m_ReceiveTagset.GetNextIndex (), m_ReceiveTagset.GetNextSessionTag (), shared_from_this ()); i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) // decrypt payload std::vector payload (len - 16); @@ -334,13 +348,26 @@ namespace garlic return true; } - bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index) + bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) { uint8_t nonce[12]; - memset (nonce, 0, 12); - htole64buf (nonce + 4, index); // tag's index + CreateNonce (m_SendTagset.GetNextIndex (), nonce); // tag's index + uint64_t tag = m_SendTagset.GetNextSessionTag (); + memcpy (out, &tag, 8); // ad = The session tag, 8 bytes // ciphertext = ENCRYPT(k, n, payload, ad) + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, m_SendKey, nonce, out + 8, outLen - 8, true)) // encrypt + { + LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); + return false; + } + return true; + } + + bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index) + { + uint8_t nonce[12]; + CreateNonce (index, nonce); // tag's index len -= 8; // tag std::vector payload (len - 16); if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 8, len - 16, buf, 8, m_ReceiveKey, nonce, payload.data (), len - 16, false)) // decrypt @@ -378,6 +405,11 @@ namespace garlic 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; diff --git a/libi2pd/ECIESX25519AEADRatchetSession.h b/libi2pd/ECIESX25519AEADRatchetSession.h index f9b2de25..acc90e96 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.h +++ b/libi2pd/ECIESX25519AEADRatchetSession.h @@ -21,6 +21,7 @@ namespace garlic void DHInitialize (const uint8_t * rootKey, const uint8_t * k); void NextSessionTagRatchet (); uint64_t GetNextSessionTag (); + int GetNextIndex () const { return m_NextIndex; }; private: @@ -35,6 +36,7 @@ namespace garlic } m_KeyData; uint8_t m_SessTagConstant[32]; + int m_NextIndex; }; enum ECIESx25519BlockType @@ -78,6 +80,7 @@ namespace garlic void ResetKeys (); void MixHash (const uint8_t * buf, size_t len); + void CreateNonce (uint64_t seqn, uint8_t * nonce); bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes uint64_t CreateNewSessionTag () const; @@ -88,6 +91,8 @@ namespace garlic bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); + bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); + std::vector CreatePayload (std::shared_ptr msg); size_t CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len, bool isDestination = false); diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index 7307b9c1..14154eaf 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -418,6 +418,8 @@ namespace garlic m_Sessions.clear (); m_DeliveryStatusSessions.clear (); m_Tags.clear (); + m_ECIESx25519Sessions.clear (); + m_ECIESx25519Tags.clear (); } void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag) { @@ -737,6 +739,7 @@ namespace garlic ++it; } } + // TODO: cleanup ECIESx25519 } void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID) @@ -935,7 +938,7 @@ namespace garlic } } - void GarlicDestination::AddECIESx25519SessionTag (uint64_t tag, int index, ECIESX25519AEADRatchetSessionPtr session) + void GarlicDestination::AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session) { m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexSession{index, session}); } diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index d12d0fa7..4fdb299e 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -213,6 +213,7 @@ namespace garlic void CleanUp (); void SetNumTags (int numTags) { m_NumTags = numTags; }; + int GetNumTags () const { return m_NumTags; }; std::shared_ptr GetRoutingSession (std::shared_ptr destination, bool attachLeaseSet); void CleanupExpiredTags (); void RemoveDeliveryStatusSession (uint32_t msgID); @@ -222,7 +223,7 @@ namespace garlic void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread void DeliveryStatusSent (ElGamalAESSessionPtr session, uint32_t msgID); - void AddECIESx25519SessionTag (uint64_t tag, int index, ECIESX25519AEADRatchetSessionPtr session); + void AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session); void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session); void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);