mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2026-08-27 22:35:02 +00:00
send ratchet session response for unidirectional traffic for I2CP
This commit is contained in:
@@ -437,7 +437,9 @@ namespace client
|
||||
{
|
||||
uint8_t pub[32];
|
||||
leaseSet->Encrypt (nullptr, pub);
|
||||
if (memcmp (from->GetRemoteStaticKey (), pub, 32))
|
||||
if (!memcmp (from->GetRemoteStaticKey (), pub, 32))
|
||||
from->SetDestination (leaseSet->GetIdentHash ());
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Destination: Remote LeaseSet static key mismatch");
|
||||
leaseSet = nullptr;
|
||||
|
||||
@@ -459,6 +459,8 @@ namespace garlic
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
if (IsResponseRequired () && GetOwner ())
|
||||
GetOwner ()->ScheduleSessionResponseTimer (shared_from_this());
|
||||
}
|
||||
|
||||
void ECIESX25519AEADRatchetSession::HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2026, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -33,7 +33,7 @@ namespace garlic
|
||||
const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds
|
||||
const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds
|
||||
const int ECIESX25519_SESSION_CREATE_TIMEOUT = 3; // in seconds, NSR must be send after NS received
|
||||
const int ECIESX25519_SESSION_ESTABLISH_TIMEOUT = 15; // in seconds
|
||||
const int ECIESX25519_SESSION_ESTABLISH_TIMEOUT = 15; // in seconds
|
||||
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // in seconds
|
||||
const int ECIESX25519_DEFAULT_ACK_REQUEST_INTERVAL = 33000; // in milliseconds
|
||||
const int ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS = 3;
|
||||
@@ -64,7 +64,7 @@ namespace garlic
|
||||
void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; };
|
||||
|
||||
uint32_t GetMsgID () const { return (m_TagSetID << 16) + m_NextIndex; }; // (tagsetid << 16) + N
|
||||
|
||||
|
||||
private:
|
||||
|
||||
i2p::data::Tag<64> m_SessionTagKeyData;
|
||||
@@ -95,7 +95,7 @@ namespace garlic
|
||||
virtual bool IsIndexExpired (int index) const;
|
||||
virtual bool HandleNextMessage (uint8_t * buf, size_t len, int index);
|
||||
virtual bool IsSessionTerminated () const;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
int m_TrimBehindIndex = 0;
|
||||
@@ -113,7 +113,7 @@ namespace garlic
|
||||
bool IsIndexExpired (int index) const override { return false; };
|
||||
bool HandleNextMessage (uint8_t * buf, size_t len, int index) override;
|
||||
bool IsSessionTerminated () const override { return false; }
|
||||
|
||||
|
||||
private:
|
||||
|
||||
GarlicDestination * m_Destination;
|
||||
@@ -172,29 +172,30 @@ namespace garlic
|
||||
|
||||
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
|
||||
i2p::data::CryptoKeyType GetRemoteStaticKeyType () const { return m_RemoteStaticKeyType; }
|
||||
void SetRemoteStaticKey (i2p::data::CryptoKeyType keyType, const uint8_t * key)
|
||||
{
|
||||
void SetRemoteStaticKey (i2p::data::CryptoKeyType keyType, const uint8_t * key)
|
||||
{
|
||||
m_RemoteStaticKeyType = keyType;
|
||||
memcpy (m_RemoteStaticKey, key, 32);
|
||||
memcpy (m_RemoteStaticKey, key, 32);
|
||||
}
|
||||
void Terminate () { m_IsTerminated = true; }
|
||||
void SetDestination (const i2p::data::IdentHash& dest)
|
||||
{
|
||||
if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest));
|
||||
}
|
||||
const i2p::data::IdentHash * GetDestinationPtr () const { return m_Destination ? m_Destination.get () : nullptr; }; // for pongs
|
||||
const i2p::data::IdentHash * GetDestinationPtr () const { return m_Destination ? m_Destination.get () : nullptr; }; // for pongs
|
||||
bool CheckExpired (uint64_t ts); // true is expired
|
||||
bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; }
|
||||
bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); }
|
||||
void CleanupReceiveNSRKeys (); // called from ReceiveRatchetTagSet at Alice's side
|
||||
|
||||
bool IsResponseRequired () const { return m_State == eSessionStateNewSessionReceived || m_SendReverseKey || !m_AckRequests.empty (); };
|
||||
|
||||
bool IsRatchets () const override { return true; };
|
||||
bool IsReadyToSend () const override { return m_State != eSessionStateNewSessionSent; };
|
||||
bool IsTerminated () const override { return m_IsTerminated; }
|
||||
uint64_t GetLastActivityTimestamp () const override { return m_LastActivityTimestamp; };
|
||||
void SetAckRequestInterval (int interval) override { m_AckRequestInterval = interval; };
|
||||
bool CleanupUnconfirmedTags () override; // return true if unaswered Ack requests, called from I2CP
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; };
|
||||
@@ -202,7 +203,7 @@ namespace garlic
|
||||
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
||||
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
|
||||
bool MessageConfirmed (uint32_t msgID) override;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
|
||||
@@ -222,13 +223,13 @@ namespace garlic
|
||||
size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len, bool alwaysLocal = false);
|
||||
size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len);
|
||||
size_t CreatePaddingClove (uint8_t paddingSize, uint8_t * buf, size_t len);
|
||||
|
||||
|
||||
void GenerateMoreReceiveTags (std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int numTags);
|
||||
void NewNextSendRatchet ();
|
||||
|
||||
std::shared_ptr<I2NPMessage> WrapPayload (const uint8_t * payload, size_t len);
|
||||
uint8_t GetNextPaddingSize (size_t payloadLen);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
i2p::data::CryptoKeyType m_RemoteStaticKeyType;
|
||||
@@ -236,10 +237,10 @@ namespace garlic
|
||||
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
|
||||
uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only
|
||||
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
||||
#if OPENSSL_PQ
|
||||
#if OPENSSL_PQ
|
||||
std::unique_ptr<i2p::crypto::MLKEMKeys> m_PQKeys;
|
||||
std::unique_ptr<std::vector<uint8_t> > m_NSREncodedPQKey;
|
||||
#endif
|
||||
#endif
|
||||
SessionState m_State = eSessionStateNew;
|
||||
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds)
|
||||
m_LastSentTimestamp = 0; // in milliseconds
|
||||
@@ -250,10 +251,10 @@ namespace garlic
|
||||
std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet;
|
||||
|
||||
uint64_t m_LastAckRequestSendTime = 0; // milliseconds
|
||||
uint32_t m_AckRequestMsgID = 0;
|
||||
uint32_t m_AckRequestMsgID = 0;
|
||||
int m_AckRequestNumAttempts = 0;
|
||||
int m_AckRequestInterval = ECIESX25519_DEFAULT_ACK_REQUEST_INTERVAL; // milliseconds
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
|
||||
+13
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2026, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -120,7 +120,7 @@ namespace garlic
|
||||
virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only
|
||||
virtual void SetAckRequestInterval (int interval) {}; // in milliseconds, override in ECIESX25519AEADRatchetSession
|
||||
virtual std::vector<std::shared_ptr<I2NPMessage> > WrapMultipleMessages (const std::vector<std::shared_ptr<const I2NPMessage> >& msgs);
|
||||
|
||||
|
||||
void SetLeaseSetUpdated ()
|
||||
{
|
||||
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
||||
@@ -138,7 +138,7 @@ namespace garlic
|
||||
|
||||
int NumSentPackets () const { return m_NumSentPackets; }
|
||||
void SetNumSentPackets (int numSentPackets) { m_NumSentPackets = numSentPackets; }
|
||||
|
||||
|
||||
GarlicDestination * GetOwner () const { return m_Owner; }
|
||||
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
|
||||
|
||||
@@ -255,11 +255,11 @@ namespace garlic
|
||||
void RemoveDeliveryStatusSession (uint32_t msgID);
|
||||
std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
|
||||
bool AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len);
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len);
|
||||
bool AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen,
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len);
|
||||
const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len);
|
||||
|
||||
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
|
||||
void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag
|
||||
@@ -278,24 +278,26 @@ namespace garlic
|
||||
|
||||
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
|
||||
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
|
||||
virtual i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const
|
||||
virtual i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const
|
||||
{
|
||||
return GetIdentity ()->GetCryptoKeyType () >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? GetIdentity ()->GetCryptoKeyType () : 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void ScheduleSessionResponseTimer (std::shared_ptr<ECIESX25519AEADRatchetSession> session) {};
|
||||
|
||||
protected:
|
||||
|
||||
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
|
||||
bool HandleECIESx25519TagMessage (uint8_t * buf, size_t len); // return true if found
|
||||
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only
|
||||
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
||||
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
||||
size_t len, uint32_t msgID, ECIESX25519AEADRatchetSession * from) = 0;
|
||||
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleDeliveryStatusMessage (uint32_t msgID);
|
||||
|
||||
void SaveTags ();
|
||||
void LoadTags ();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
bool SupportsRatchets () const { return GetRatchetsHighestCryptoType () > 0; }
|
||||
@@ -323,7 +325,7 @@ namespace garlic
|
||||
// encryption
|
||||
i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor;
|
||||
i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
|
||||
@@ -128,6 +128,38 @@ namespace client
|
||||
return cryptoType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? cryptoType : 0;
|
||||
}
|
||||
|
||||
void I2CPDestination::ScheduleSessionResponseTimer (std::shared_ptr<i2p::garlic::ECIESX25519AEADRatchetSession> session)
|
||||
{
|
||||
if (!session) return;
|
||||
auto timer = std::make_shared<boost::asio::steady_timer>(GetService ());
|
||||
timer->expires_after (std::chrono::milliseconds(I2CP_RATCHETS_RESPONSE_TIMEOUT));
|
||||
timer->async_wait ([session, s = GetSharedFromThis (), timer](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted &&
|
||||
!session->IsTerminated () && session->IsResponseRequired () && // response still required
|
||||
session->GetDestinationPtr ()) // we know ident hash
|
||||
{
|
||||
auto pool = s->GetTunnelPool ();
|
||||
if (!pool) return;
|
||||
auto ls = s->FindLeaseSet (*session->GetDestinationPtr ());
|
||||
if (!ls) return;
|
||||
auto garlic = session->WrapSingleMessage (nullptr); // no data messages
|
||||
if (!garlic) return;
|
||||
auto leases = ls->GetNonExpiredLeases (false);
|
||||
if (!leases.empty ())
|
||||
{
|
||||
auto remoteLease = leases[s->GetRng ()() % leases.size ()];
|
||||
auto outboundTunnel = pool->GetNextOutboundTunnel ();
|
||||
if (outboundTunnel)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2CP: Sending ratchets session response");
|
||||
s->SendMsg (garlic, outboundTunnel, remoteLease);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len,
|
||||
i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2026, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -34,6 +34,7 @@ namespace client
|
||||
const int I2CP_LEASESET_CREATION_TIMEOUT = 10; // in seconds
|
||||
const int I2CP_DESTINATION_READINESS_CHECK_INTERVAL = 5; // in seconds
|
||||
const int I2CP_SESSION_ACK_REQUEST_INTERVAL = 12100; // in milliseconds
|
||||
const int I2CP_RATCHETS_RESPONSE_TIMEOUT = 1000; // in milliseconds
|
||||
|
||||
const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
|
||||
const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4;
|
||||
@@ -86,7 +87,7 @@ namespace client
|
||||
public:
|
||||
|
||||
I2CPDestination (boost::asio::io_context& service, std::shared_ptr<I2CPSession> owner,
|
||||
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, bool isSameThread,
|
||||
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, bool isSameThread,
|
||||
const i2p::util::Mapping& params);
|
||||
~I2CPDestination () {};
|
||||
|
||||
@@ -98,7 +99,7 @@ namespace client
|
||||
void LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len); // called from I2CPSession
|
||||
void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession
|
||||
bool SendMsg (const uint8_t * payload, size_t len, std::shared_ptr<i2p::garlic::GarlicRoutingSession> remoteSession, uint32_t nonce);
|
||||
|
||||
|
||||
// implements LocalDestination
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override;
|
||||
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const override;
|
||||
@@ -109,21 +110,22 @@ namespace client
|
||||
|
||||
// GarlicDestination
|
||||
i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const override;
|
||||
void ScheduleSessionResponseTimer (std::shared_ptr<i2p::garlic::ECIESX25519AEADRatchetSession> session) override;
|
||||
// LeaseSetDestination
|
||||
void CleanupDestination () override;
|
||||
i2p::data::CryptoKeyType GetPreferredCryptoType () const override;
|
||||
// I2CP
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len, i2p::garlic::ECIESX25519AEADRatchetSession * from) override;
|
||||
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) override;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<I2CPDestination> GetSharedFromThis ()
|
||||
{ return std::static_pointer_cast<I2CPDestination>(shared_from_this ()); }
|
||||
bool SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
|
||||
bool SendMsg (std::shared_ptr<I2NPMessage> garlic,
|
||||
bool SendMsg (std::shared_ptr<I2NPMessage> garlic,
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel, std::shared_ptr<const i2p::data::Lease> remoteLease);
|
||||
|
||||
|
||||
void PostCreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||
|
||||
private:
|
||||
@@ -197,7 +199,7 @@ namespace client
|
||||
|
||||
void SendSessionStatusMessage (I2CPSessionStatus status);
|
||||
void SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
I2CPServer& m_Owner;
|
||||
|
||||
Reference in New Issue
Block a user