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