1 /* 2 * Copyright (c) 2013-2021, 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 ECIES_X25519_AEAD_RATCHET_SESSION_H__ 10 #define ECIES_X25519_AEAD_RATCHET_SESSION_H__ 11 12 #include <string.h> 13 #include <inttypes.h> 14 #include <functional> 15 #include <memory> 16 #include <vector> 17 #include <list> 18 #include <unordered_map> 19 #include "Identity.h" 20 #include "Crypto.h" 21 #include "Garlic.h" 22 #include "Tag.h" 23 24 namespace i2p 25 { 26 namespace garlic 27 { 28 const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second since session creation we can restart session after 29 const int ECIESX25519_INACTIVITY_TIMEOUT = 90; // number of seconds we receive nothing and should restart if we can 30 const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after 31 const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds 32 const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds 33 const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180 34 const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after 35 const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24; 36 const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 320; 37 const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12; 38 39 const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */ 40 // - 16 /* I2NP header */ - 16 /* poly hash */ - 8 /* tag */ - 4 /* garlic length */ 41 42 class RatchetTagSet 43 { 44 public: 45 RatchetTagSet()46 RatchetTagSet () {}; ~RatchetTagSet()47 virtual ~RatchetTagSet () {}; 48 49 void DHInitialize (const uint8_t * rootKey, const uint8_t * k); 50 void NextSessionTagRatchet (); 51 uint64_t GetNextSessionTag (); GetNextRootKey()52 const uint8_t * GetNextRootKey () const { return m_NextRootKey; }; GetNextIndex()53 int GetNextIndex () const { return m_NextIndex; }; 54 void GetSymmKey (int index, uint8_t * key); 55 void DeleteSymmKey (int index); 56 GetTagSetID()57 int GetTagSetID () const { return m_TagSetID; }; SetTagSetID(int tagsetID)58 void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; }; 59 60 private: 61 62 i2p::data::Tag<64> m_SessionTagKeyData; 63 uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32]; 64 int m_NextIndex, m_NextSymmKeyIndex; 65 std::unordered_map<int, i2p::data::Tag<32> > m_ItermediateSymmKeys; 66 67 int m_TagSetID = 0; 68 }; 69 70 class ECIESX25519AEADRatchetSession; 71 class ReceiveRatchetTagSet: public RatchetTagSet, 72 public std::enable_shared_from_this<ReceiveRatchetTagSet> 73 { 74 public: 75 76 ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false): m_Session(session)77 m_Session (session), m_IsNS (isNS) {}; 78 IsNS()79 bool IsNS () const { return m_IsNS; }; GetSession()80 std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session; }; SetTrimBehind(int index)81 void SetTrimBehind (int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; }; GetTrimBehind()82 int GetTrimBehind () const { return m_TrimBehindIndex; }; 83 84 void Expire (); 85 bool IsExpired (uint64_t ts) const; 86 87 virtual bool IsIndexExpired (int index) const; 88 virtual bool HandleNextMessage (uint8_t * buf, size_t len, int index); 89 90 private: 91 92 int m_TrimBehindIndex = 0; 93 std::shared_ptr<ECIESX25519AEADRatchetSession> m_Session; 94 bool m_IsNS; 95 uint64_t m_ExpirationTimestamp = 0; 96 }; 97 98 class SymmetricKeyTagSet: public ReceiveRatchetTagSet 99 { 100 public: 101 102 SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key); 103 IsIndexExpired(int index)104 bool IsIndexExpired (int index) const { return false; }; 105 bool HandleNextMessage (uint8_t * buf, size_t len, int index); 106 107 private: 108 109 GarlicDestination * m_Destination; 110 uint8_t m_Key[32]; 111 }; 112 113 enum ECIESx25519BlockType 114 { 115 eECIESx25519BlkDateTime = 0, 116 eECIESx25519BlkSessionID = 1, 117 eECIESx25519BlkTermination = 4, 118 eECIESx25519BlkOptions = 5, 119 eECIESx25519BlkNextKey = 7, 120 eECIESx25519BlkAck = 8, 121 eECIESx25519BlkAckRequest = 9, 122 eECIESx25519BlkGalicClove = 11, 123 eECIESx25519BlkPadding = 254 124 }; 125 126 const uint8_t ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG = 0x01; 127 const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02; 128 const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04; 129 130 class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, 131 private i2p::crypto::NoiseSymmetricState, 132 public std::enable_shared_from_this<ECIESX25519AEADRatchetSession> 133 { 134 enum SessionState 135 { 136 eSessionStateNew = 0, 137 eSessionStateNewSessionReceived, 138 eSessionStateNewSessionSent, 139 eSessionStateNewSessionReplySent, 140 eSessionStateEstablished, 141 eSessionStateOneTime 142 }; 143 144 struct DHRatchet 145 { 146 int keyID = 0; 147 std::shared_ptr<i2p::crypto::X25519Keys> key; 148 uint8_t remote[32]; // last remote public key 149 bool newKey = true; 150 }; 151 152 public: 153 154 ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS); 155 ~ECIESX25519AEADRatchetSession (); 156 157 bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0); 158 std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg); 159 std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg); 160 GetRemoteStaticKey()161 const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } SetRemoteStaticKey(const uint8_t * key)162 void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } 163 Terminate()164 void Terminate () { m_IsTerminated = true; } SetDestination(const i2p::data::IdentHash & dest)165 void SetDestination (const i2p::data::IdentHash& dest) // TODO: 166 { 167 if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest)); 168 } 169 170 bool CheckExpired (uint64_t ts); // true is expired CanBeRestarted(uint64_t ts)171 bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; } IsInactive(uint64_t ts)172 bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); } 173 IsRatchets()174 bool IsRatchets () const { return true; }; IsReadyToSend()175 bool IsReadyToSend () const { return m_State != eSessionStateNewSessionSent; }; IsTerminated()176 bool IsTerminated () const { return m_IsTerminated; } GetLastActivityTimestamp()177 uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; }; 178 179 protected: 180 GetNoiseState()181 i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; }; SetNoiseState(const i2p::crypto::NoiseSymmetricState & state)182 void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; }; 183 void CreateNonce (uint64_t seqn, uint8_t * nonce); 184 void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index); 185 186 private: 187 188 bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes 189 void InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const; 190 191 bool HandleNewIncomingSession (const uint8_t * buf, size_t len); 192 bool HandleNewOutgoingSessionReply (uint8_t * buf, size_t len); 193 bool HandleExistingSessionMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index); 194 void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset); 195 196 bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true); 197 bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); 198 bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); 199 bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); 200 201 size_t CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first, uint8_t * payload); 202 size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len); 203 size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len); 204 205 void GenerateMoreReceiveTags (std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int numTags); 206 void NewNextSendRatchet (); 207 208 private: 209 210 uint8_t m_RemoteStaticKey[32]; 211 uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only 212 uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only 213 std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys; 214 SessionState m_State = eSessionStateNew; 215 uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds) 216 m_LastSentTimestamp = 0; // in milliseconds 217 std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset; 218 std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it 219 std::list<std::pair<uint16_t, int> > m_AckRequests; // (tagsetid, index) 220 bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false; 221 std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet; 222 uint8_t m_PaddingSizes[32], m_NextPaddingSize; 223 224 public: 225 226 // for HTTP only GetState()227 int GetState () const { return (int)m_State; } GetDestination()228 i2p::data::IdentHash GetDestination () const 229 { 230 return m_Destination ? *m_Destination : i2p::data::IdentHash (); 231 } 232 }; 233 234 // single session for all incoming messages 235 class RouterIncomingRatchetSession: public ECIESX25519AEADRatchetSession 236 { 237 public: 238 239 RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState); 240 bool HandleNextMessage (const uint8_t * buf, size_t len); GetCurrentNoiseState()241 i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; }; 242 243 private: 244 245 i2p::crypto::NoiseSymmetricState m_CurrentNoiseState; 246 }; 247 248 std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag); 249 std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<const I2NPMessage> msg, const uint8_t * routerPublicKey); 250 } 251 } 252 253 #endif 254 255