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 
10 #include <memory>
11 #include <openssl/rand.h>
12 #include <openssl/sha.h>
13 #include "Log.h"
14 #include "Transports.h"
15 #include "Timestamp.h"
16 #include "I2PEndian.h"
17 #include "I2NPProtocol.h"
18 #include "TunnelConfig.h"
19 
20 namespace i2p
21 {
22 namespace tunnel
23 {
TunnelHopConfig(std::shared_ptr<const i2p::data::IdentityEx> r)24 	TunnelHopConfig::TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r)
25 	{
26 		RAND_bytes ((uint8_t *)&tunnelID, 4);
27 		if (!tunnelID) tunnelID = 1; // tunnelID can't be zero
28 		isGateway = true;
29 		isEndpoint = true;
30 		ident = r;
31 		//nextRouter = nullptr;
32 		nextTunnelID = 0;
33 
34 		next = nullptr;
35 		prev = nullptr;
36 	}
37 
SetNextIdent(const i2p::data::IdentHash & ident)38 	void TunnelHopConfig::SetNextIdent (const i2p::data::IdentHash& ident)
39 	{
40 		nextIdent = ident;
41 		isEndpoint = false;
42 		RAND_bytes ((uint8_t *)&nextTunnelID, 4);
43 		if (!nextTunnelID) nextTunnelID = 1; // tunnelID can't be zero
44 	}
45 
SetReplyHop(uint32_t replyTunnelID,const i2p::data::IdentHash & replyIdent)46 	void TunnelHopConfig::SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent)
47 	{
48 		nextIdent = replyIdent;
49 		nextTunnelID = replyTunnelID;
50 		isEndpoint = true;
51 	}
52 
SetNext(TunnelHopConfig * n)53 	void TunnelHopConfig::SetNext (TunnelHopConfig * n)
54 	{
55 		next = n;
56 		if (next)
57 		{
58 			next->prev = this;
59 			next->isGateway = false;
60 			isEndpoint = false;
61 			nextIdent = next->ident->GetIdentHash ();
62 			nextTunnelID = next->tunnelID;
63 		}
64 	}
65 
SetPrev(TunnelHopConfig * p)66 	void TunnelHopConfig::SetPrev (TunnelHopConfig * p)
67 	{
68 		prev = p;
69 		if (prev)
70 		{
71 			prev->next = this;
72 			prev->isEndpoint = false;
73 			isGateway = false;
74 		}
75 	}
76 
DecryptRecord(uint8_t * records,int index) const77 	void TunnelHopConfig::DecryptRecord (uint8_t * records, int index) const
78 	{
79 		uint8_t * record = records + index*TUNNEL_BUILD_RECORD_SIZE;
80 		i2p::crypto::CBCDecryption decryption;
81 		decryption.SetKey (replyKey);
82 		decryption.SetIV (replyIV);
83 		decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
84 	}
85 
EncryptECIES(const uint8_t * plainText,size_t len,uint8_t * encrypted)86 	void ECIESTunnelHopConfig::EncryptECIES (const uint8_t * plainText, size_t len, uint8_t * encrypted)
87 	{
88 		if (!ident) return;
89 		i2p::crypto::InitNoiseNState (*this, ident->GetEncryptionPublicKey ());
90 		auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
91 		memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32);
92 		MixHash (encrypted, 32); // h = SHA256(h || sepk)
93 		encrypted += 32;
94 		uint8_t sharedSecret[32];
95 		ephemeralKeys->Agree (ident->GetEncryptionPublicKey (), sharedSecret); // x25519(sesk, hepk)
96 		MixKey (sharedSecret);
97 		uint8_t nonce[12];
98 		memset (nonce, 0, 12);
99 		if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, len, m_H, 32, m_CK + 32, nonce, encrypted, len + 16, true)) // encrypt
100 		{
101 			LogPrint (eLogWarning, "Tunnel: Plaintext AEAD encryption failed");
102 			return;
103 		}
104 		MixHash (encrypted, len + 16); // h = SHA256(h || ciphertext)
105 	}
106 
DecryptECIES(const uint8_t * key,const uint8_t * nonce,const uint8_t * encrypted,size_t len,uint8_t * clearText) const107 	bool ECIESTunnelHopConfig::DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const
108 	{
109 		return i2p::crypto::AEADChaCha20Poly1305 (encrypted, len - 16, m_H, 32, key, nonce, clearText, len - 16, false); // decrypt
110 	}
111 
CreateBuildRequestRecord(uint8_t * records,uint32_t replyMsgID)112 	void LongECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID)
113 	{
114 		// generate keys
115 		RAND_bytes (layerKey, 32);
116 		RAND_bytes (ivKey, 32);
117 		RAND_bytes (replyKey, 32);
118 		RAND_bytes (replyIV, 16);
119 		// fill clear text
120 		uint8_t flag = 0;
121 		if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG;
122 		if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
123 		uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
124 		htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
125 		htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
126 		memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
127 		memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
128 		memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
129 		memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
130 		memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
131 		clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
132 		memset (clearText + ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 3); // set to 0 for compatibility
133 		htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ());
134 		htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes
135 		htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
136 		memset (clearText + ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET, 0, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET);
137 		// encrypt
138 		uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE;
139 		EncryptECIES (clearText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
140 		memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
141 	}
142 
DecryptBuildResponseRecord(uint8_t * records) const143 	bool LongECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const
144 	{
145 		uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE;
146 		uint8_t nonce[12];
147 		memset (nonce, 0, 12);
148 		if (!DecryptECIES (m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE, record))
149 		{
150 			LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
151 			return false;
152 		}
153 		return true;
154 	}
155 
CreateBuildRequestRecord(uint8_t * records,uint32_t replyMsgID)156 	void ShortECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID)
157 	{
158 		// fill clear text
159 		uint8_t flag = 0;
160 		if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG;
161 		if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
162 		uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE ];
163 		htobe32buf (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
164 		htobe32buf (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
165 		memcpy (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
166 		clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] = flag;
167 		memset (clearText + SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 2);
168 		clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE] = 0; // AES
169 		htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ());
170         htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET , 600); // +10 minutes
171 		htobe32buf (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
172 		memset (clearText + SHORT_REQUEST_RECORD_PADDING_OFFSET, 0, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE - SHORT_REQUEST_RECORD_PADDING_OFFSET);
173 		// encrypt
174 		uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE;
175 		EncryptECIES (clearText, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET);
176 		// derive keys
177 		i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelReplyKey", m_CK);
178 		memcpy (replyKey, m_CK + 32, 32);
179 		i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelLayerKey", m_CK);
180 		memcpy (layerKey, m_CK + 32, 32);
181 		if (isEndpoint)
182 		{
183 			i2p::crypto::HKDF (m_CK, nullptr, 0, "TunnelLayerIVKey", m_CK);
184 			memcpy (ivKey, m_CK + 32, 32);
185 			i2p::crypto::HKDF (m_CK, nullptr, 0, "RGarlicKeyAndTag", m_CK); // OTBRM garlic key m_CK + 32, tag first 8 bytes of m_CK
186 		}
187 		else
188 			memcpy (ivKey, m_CK, 32); // last HKDF
189 		memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
190 	}
191 
DecryptBuildResponseRecord(uint8_t * records) const192 	bool ShortECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const
193 	{
194 		uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE;
195 		uint8_t nonce[12];
196 		memset (nonce, 0, 12);
197 		nonce[4] = recordIndex; // nonce is record index
198 		if (!DecryptECIES (replyKey, nonce, record, SHORT_TUNNEL_BUILD_RECORD_SIZE, record))
199 		{
200 			LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
201 			return false;
202 		}
203 		return true;
204 	}
205 
DecryptRecord(uint8_t * records,int index) const206 	void ShortECIESTunnelHopConfig::DecryptRecord (uint8_t * records, int index) const
207 	{
208 		uint8_t * record = records + index*SHORT_TUNNEL_BUILD_RECORD_SIZE;
209 		uint8_t nonce[12];
210 		memset (nonce, 0, 12);
211 		nonce[4] = index; // nonce is index
212 		i2p::crypto::ChaCha20 (record, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, record);
213 	}
214 
GetGarlicKey(uint8_t * key) const215 	uint64_t ShortECIESTunnelHopConfig::GetGarlicKey (uint8_t * key) const
216 	{
217 		uint64_t tag;
218 		memcpy (&tag, m_CK, 8);
219 		memcpy (key, m_CK + 32, 32);
220 		return tag;
221 	}
222 
CreatePeers(const std::vector<std::shared_ptr<const i2p::data::IdentityEx>> & peers)223 	void TunnelConfig::CreatePeers (const std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers)
224 	{
225 		TunnelHopConfig * prev = nullptr;
226 		for (const auto& it: peers)
227 		{
228 			TunnelHopConfig * hop = nullptr;
229 			if (m_IsShort)
230 				hop = new ShortECIESTunnelHopConfig (it);
231 			else
232 			{
233 				if (it->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
234 					hop = new LongECIESTunnelHopConfig (it);
235 				else
236 					LogPrint (eLogError, "Tunnel: ElGamal router is not supported");
237 			}
238 			if (hop)
239 			{
240 				if (prev)
241 					prev->SetNext (hop);
242 				else
243 					m_FirstHop = hop;
244 				prev = hop;
245 			}
246 		}
247 		m_LastHop = prev;
248 	}
249 }
250 }