Garlic.h
1 /* 2 * Copyright (c) 2013-2026, The PurpleI2P Project 3 * 4 * This file is part of Purple i2pd project and licensed under BSD3 5 * 6 * See full license text in LICENSE file at top of project tree 7 */ 8 9 #ifndef GARLIC_H__ 10 #define GARLIC_H__ 11 12 #include <inttypes.h> 13 #include <unordered_map> 14 #include <list> 15 #include <string> 16 #include <thread> 17 #include <mutex> 18 #include <memory> 19 #include <random> 20 #include "Crypto.h" 21 #include "I2NPProtocol.h" 22 #include "LeaseSet.h" 23 #include "Queue.h" 24 #include "Identity.h" 25 26 namespace i2p 27 { 28 namespace tunnel 29 { 30 class OutboundTunnel; 31 } 32 33 namespace garlic 34 { 35 36 enum GarlicDeliveryType 37 { 38 eGarlicDeliveryTypeLocal = 0, 39 eGarlicDeliveryTypeDestination = 1, 40 eGarlicDeliveryTypeRouter = 2, 41 eGarlicDeliveryTypeTunnel = 3 42 }; 43 44 struct ElGamalBlock 45 { 46 uint8_t sessionKey[32]; 47 uint8_t preIV[32]; 48 uint8_t padding[158]; 49 }; 50 51 const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes 52 const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes 53 const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds 54 const int LEASESET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds 55 const int ROUTING_PATH_EXPIRATION_TIMEOUT = 120; // in seconds 56 const int INCOMING_SESSIONS_MINIMAL_INTERVAL = 200; // in milliseconds 57 58 struct SessionTag: public i2p::data::Tag<32> 59 { 60 SessionTag (const uint8_t * buf, uint32_t ts = 0): Tag<32>(buf), creationTime (ts) {}; 61 SessionTag () = default; 62 SessionTag (const SessionTag& ) = default; 63 SessionTag& operator= (const SessionTag& ) = default; 64 #ifndef _WIN32 65 SessionTag (SessionTag&& ) = default; 66 SessionTag& operator= (SessionTag&& ) = default; 67 #endif 68 uint32_t creationTime; // seconds since epoch 69 }; 70 71 // AESDecryption is associated with session tags and store key 72 class AESDecryption: public i2p::crypto::CBCDecryption 73 { 74 public: 75 76 AESDecryption (const uint8_t * key): m_Key (key) 77 { 78 SetKey (key); 79 } 80 const i2p::crypto::AESKey& GetKey () const { return m_Key; }; 81 82 private: 83 84 i2p::crypto::AESKey m_Key; 85 }; 86 87 struct GarlicRoutingPath 88 { 89 std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel; 90 std::shared_ptr<const i2p::data::Lease> remoteLease; 91 // for streaming only 92 int rtt; // RTT 93 uint32_t updateTime; // seconds since epoch 94 }; 95 96 class GarlicDestination; 97 class GarlicRoutingSession 98 { 99 protected: 100 101 enum LeaseSetUpdateStatus 102 { 103 eLeaseSetUpToDate = 0, 104 eLeaseSetUpdated, 105 eLeaseSetSubmitted, 106 eLeaseSetDoNotSend 107 }; 108 109 public: 110 111 GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet); 112 GarlicRoutingSession (); 113 virtual ~GarlicRoutingSession (); 114 virtual std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) = 0; 115 virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession and ECIESX25519AEADRatchetSession 116 virtual bool MessageConfirmed (uint32_t msgID); 117 virtual bool IsRatchets () const { return false; }; 118 virtual bool IsReadyToSend () const { return true; }; 119 virtual bool IsTerminated () const { return !GetOwner (); }; 120 virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only 121 virtual void SetAckRequestInterval (int interval) {}; // in milliseconds, override in ECIESX25519AEADRatchetSession 122 virtual std::vector<std::shared_ptr<I2NPMessage> > WrapMultipleMessages (const std::vector<std::shared_ptr<const I2NPMessage> >& msgs); 123 124 void SetLeaseSetUpdated () 125 { 126 if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated; 127 }; 128 bool IsLeaseSetNonConfirmed () const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; }; 129 bool IsLeaseSetUpdated () const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; }; 130 uint64_t GetLeaseSetSubmissionTime () const { return m_LeaseSetSubmissionTime; } 131 void CleanupUnconfirmedLeaseSet (uint64_t ts); 132 133 std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath (); 134 void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path); 135 136 bool IsWithJava () const { return m_IsWithJava; } 137 void SetIsWithJava (bool isWithJava) { m_IsWithJava = isWithJava; } 138 139 int NumSentPackets () const { return m_NumSentPackets; } 140 void SetNumSentPackets (int numSentPackets) { m_NumSentPackets = numSentPackets; } 141 142 GarlicDestination * GetOwner () const { return m_Owner; } 143 void SetOwner (GarlicDestination * owner) { m_Owner = owner; } 144 145 protected: 146 147 LeaseSetUpdateStatus GetLeaseSetUpdateStatus () const { return m_LeaseSetUpdateStatus; } 148 void SetLeaseSetUpdateStatus (LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; } 149 uint32_t GetLeaseSetUpdateMsgID () const { return m_LeaseSetUpdateMsgID; } 150 void SetLeaseSetUpdateMsgID (uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; } 151 void SetLeaseSetSubmissionTime (uint64_t ts) { m_LeaseSetSubmissionTime = ts; } 152 153 std::shared_ptr<I2NPMessage> CreateEncryptedDeliveryStatusMsg (uint32_t msgID); 154 155 private: 156 157 GarlicDestination * m_Owner; 158 159 LeaseSetUpdateStatus m_LeaseSetUpdateStatus; 160 uint32_t m_LeaseSetUpdateMsgID; 161 uint64_t m_LeaseSetSubmissionTime; // in milliseconds 162 163 std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath; 164 bool m_IsWithJava; // based on choked value from streaming 165 int m_NumSentPackets; // for limit number of sent messages in streaming 166 167 public: 168 169 // for HTTP only 170 virtual size_t GetNumOutgoingTags () const { return 0; }; 171 }; 172 //using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>; 173 typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8 174 175 class ElGamalAESSession: public GarlicRoutingSession, public std::enable_shared_from_this<ElGamalAESSession> 176 { 177 struct UnconfirmedTags 178 { 179 UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; }; 180 ~UnconfirmedTags () { delete[] sessionTags; }; 181 uint32_t msgID; 182 int numTags; 183 SessionTag * sessionTags; 184 uint32_t tagsCreationTime; 185 }; 186 187 public: 188 189 ElGamalAESSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination, 190 int numTags, bool attachLeaseSet); 191 ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption 192 ~ElGamalAESSession () {}; 193 194 std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg); 195 196 bool MessageConfirmed (uint32_t msgID); 197 bool CleanupExpiredTags (); // returns true if something left 198 bool CleanupUnconfirmedTags (); // returns true if something has been deleted 199 200 private: 201 202 size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg); 203 size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags); 204 size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination); 205 size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID); 206 207 void TagsConfirmed (uint32_t msgID); 208 UnconfirmedTags * GenerateSessionTags (); 209 210 private: 211 212 std::shared_ptr<const i2p::data::RoutingDestination> m_Destination; 213 214 i2p::crypto::AESKey m_SessionKey; 215 std::list<SessionTag> m_SessionTags; 216 int m_NumTags; 217 std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags 218 219 i2p::crypto::CBCEncryption m_Encryption; 220 i2p::data::Tag<16> m_IV; 221 222 public: 223 224 // for HTTP only 225 size_t GetNumOutgoingTags () const { return m_SessionTags.size (); }; 226 }; 227 typedef std::shared_ptr<ElGamalAESSession> ElGamalAESSessionPtr; 228 229 class ECIESX25519AEADRatchetSession; 230 typedef std::shared_ptr<ECIESX25519AEADRatchetSession> ECIESX25519AEADRatchetSessionPtr; 231 class ReceiveRatchetTagSet; 232 typedef std::shared_ptr<ReceiveRatchetTagSet> ReceiveRatchetTagSetPtr; 233 struct ECIESX25519AEADRatchetIndexTagset 234 { 235 int index; 236 ReceiveRatchetTagSetPtr tagset; // null if used 237 }; 238 239 class GarlicDestination: public i2p::data::LocalDestination 240 { 241 public: 242 243 GarlicDestination (); 244 ~GarlicDestination (); 245 246 void CleanUp (); 247 std::mt19937& GetRng () { return m_Rng; }; 248 void SetNumTags (int numTags) { m_NumTags = numTags; }; 249 int GetNumTags () const { return m_NumTags; }; 250 void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; }; 251 int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; }; 252 std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, 253 bool attachLeaseSet, bool requestNewIfNotFound = true); 254 void CleanupExpiredTags (); 255 void RemoveDeliveryStatusSession (uint32_t msgID); 256 std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router, 257 std::shared_ptr<I2NPMessage> msg); 258 259 bool AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, 260 const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); 261 bool AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, 262 const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); 263 264 void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag 265 void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag 266 virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread 267 virtual void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag); // from different thread 268 void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID); 269 uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset); 270 void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session); 271 void RemoveECIESx25519Session (const uint8_t * staticKey); 272 void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len, ECIESX25519AEADRatchetSession * from); 273 uint8_t * GetPayloadBuffer (); 274 275 virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); 276 virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); 277 virtual void SetLeaseSetUpdated (bool post = false); 278 279 virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO 280 virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0; 281 virtual i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const 282 { 283 return GetIdentity ()->GetCryptoKeyType () >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? GetIdentity ()->GetCryptoKeyType () : 0; 284 } 285 286 virtual void ScheduleSessionResponseTimer (std::shared_ptr<ECIESX25519AEADRatchetSession> session) {}; 287 288 protected: 289 290 void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag 291 bool HandleECIESx25519TagMessage (uint8_t * buf, size_t len); // return true if found 292 virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only 293 virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, 294 size_t len, uint32_t msgID, ECIESX25519AEADRatchetSession * from) = 0; 295 void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg); 296 void HandleDeliveryStatusMessage (uint32_t msgID); 297 298 void SaveTags (); 299 void LoadTags (); 300 301 private: 302 303 bool SupportsRatchets () const { return GetRatchetsHighestCryptoType () > 0; } 304 void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption, 305 std::shared_ptr<i2p::tunnel::InboundTunnel> from); 306 void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from); 307 308 private: 309 310 std::mt19937 m_Rng; 311 // outgoing sessions 312 int m_NumTags; 313 std::mutex m_SessionsMutex; 314 std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions; 315 std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session 316 uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet 317 uint64_t m_LastIncomingSessionTimestamp; // in milliseconds 318 // incoming 319 int m_NumRatchetInboundTags; 320 std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags; 321 std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session 322 // DeliveryStatus 323 std::mutex m_DeliveryStatusSessionsMutex; 324 std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session 325 // encryption 326 i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor; 327 i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor; 328 329 public: 330 331 // for HTTP only 332 size_t GetNumIncomingTags () const { return m_Tags.size (); } 333 size_t GetNumIncomingECIESx25519Tags () const { return m_ECIESx25519Tags.size (); } 334 const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; 335 const decltype(m_ECIESx25519Sessions)& GetECIESx25519Sessions () const { return m_ECIESx25519Sessions; } 336 }; 337 338 void CleanUpTagsFiles (); 339 340 } 341 } 342 343 #endif