/* * Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree */ #ifndef TUNNEL_CONFIG_H__ #define TUNNEL_CONFIG_H__ #include #include "Identity.h" #include "RouterContext.h" #include "Crypto.h" namespace i2p { namespace tunnel { struct TunnelHopConfig { std::shared_ptr ident; i2p::data::IdentHash nextIdent; uint32_t tunnelID, nextTunnelID; uint8_t layerKey[32]; uint8_t ivKey[32]; uint8_t replyKey[32]; uint8_t replyIV[16]; bool isGateway, isEndpoint; TunnelHopConfig * next, * prev; int recordIndex; // record # in tunnel build message TunnelHopConfig (std::shared_ptr r); virtual ~TunnelHopConfig () {}; void SetNextIdent (const i2p::data::IdentHash& ident); void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent); void SetNext (TunnelHopConfig * n); void SetPrev (TunnelHopConfig * p); virtual uint8_t GetRetCode (const uint8_t * records) const = 0; virtual void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) = 0; virtual bool DecryptBuildResponseRecord (uint8_t * records) const = 0; virtual void DecryptRecord (uint8_t * records, int index) const; // AES virtual uint64_t GetGarlicKey (uint8_t * key) const { return 0; }; // return tag }; struct ECIESTunnelHopConfig: public TunnelHopConfig, public i2p::crypto::NoiseSymmetricState { ECIESTunnelHopConfig (std::shared_ptr r): TunnelHopConfig (r) {}; void EncryptECIES (const uint8_t * clearText, size_t len, uint8_t * encrypted); bool DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const; }; struct LongECIESTunnelHopConfig: public ECIESTunnelHopConfig { LongECIESTunnelHopConfig (std::shared_ptr r): ECIESTunnelHopConfig (r) {}; uint8_t GetRetCode (const uint8_t * records) const override { return (records + recordIndex*TUNNEL_BUILD_RECORD_SIZE)[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET]; }; void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override; bool DecryptBuildResponseRecord (uint8_t * records) const override; }; struct ShortECIESTunnelHopConfig: public ECIESTunnelHopConfig { ShortECIESTunnelHopConfig (std::shared_ptr r): ECIESTunnelHopConfig (r) {}; uint8_t GetRetCode (const uint8_t * records) const override { return (records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE)[SHORT_RESPONSE_RECORD_RET_OFFSET]; }; void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override; bool DecryptBuildResponseRecord (uint8_t * records) const override; void DecryptRecord (uint8_t * records, int index) const override; // Chacha20 uint64_t GetGarlicKey (uint8_t * key) const override; }; class TunnelConfig { public: TunnelConfig (const std::vector >& peers, bool isShort, i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports): // inbound m_IsShort (isShort), m_FarEndTransports (farEndTransports) { CreatePeers (peers); m_LastHop->SetNextIdent (i2p::context.GetIdentHash ()); } TunnelConfig (const std::vector >& peers, uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent, bool isShort, i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports): // outbound m_IsShort (isShort), m_FarEndTransports (farEndTransports) { CreatePeers (peers); m_FirstHop->isGateway = false; m_LastHop->SetReplyHop (replyTunnelID, replyIdent); } virtual ~TunnelConfig () { TunnelHopConfig * hop = m_FirstHop; while (hop) { auto tmp = hop; hop = hop->next; delete tmp; } } bool IsShort () const { return m_IsShort; } i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const { return m_FarEndTransports; } TunnelHopConfig * GetFirstHop () const { return m_FirstHop; } TunnelHopConfig * GetLastHop () const { return m_LastHop; } int GetNumHops () const { int num = 0; TunnelHopConfig * hop = m_FirstHop; while (hop) { num++; hop = hop->next; } return num; } bool IsEmpty () const { return !m_FirstHop; } virtual bool IsInbound () const { return m_FirstHop->isGateway; } virtual uint32_t GetTunnelID () const { if (!m_FirstHop) return 0; return IsInbound () ? m_LastHop->nextTunnelID : m_FirstHop->tunnelID; } virtual uint32_t GetNextTunnelID () const { if (!m_FirstHop) return 0; return m_FirstHop->tunnelID; } virtual const i2p::data::IdentHash& GetNextIdentHash () const { return m_FirstHop->ident->GetIdentHash (); } virtual const i2p::data::IdentHash& GetLastIdentHash () const { return m_LastHop->ident->GetIdentHash (); } std::vector > GetPeers () const { std::vector > peers; TunnelHopConfig * hop = m_FirstHop; while (hop) { peers.push_back (hop->ident); hop = hop->next; } return peers; } size_t GetRecordSize () const { return m_IsShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; }; protected: // this constructor can't be called from outside TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr), m_IsShort (false), m_FarEndTransports (i2p::data::RouterInfo::eAllTransports) { } private: void CreatePeers (const std::vector >& peers); private: TunnelHopConfig * m_FirstHop, * m_LastHop; bool m_IsShort; i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; }; class ZeroHopsTunnelConfig: public TunnelConfig { public: ZeroHopsTunnelConfig () { RAND_bytes ((uint8_t *)&m_TunnelID, 4);}; bool IsInbound () const { return true; }; // TODO: uint32_t GetTunnelID () const { return m_TunnelID; }; uint32_t GetNextTunnelID () const { return m_TunnelID; }; const i2p::data::IdentHash& GetNextIdentHash () const { return i2p::context.GetIdentHash (); }; const i2p::data::IdentHash& GetLastIdentHash () const { return i2p::context.GetIdentHash (); }; private: uint32_t m_TunnelID; }; } } #endif