1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
10 //
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
24 //
25 
26 /* Basic Obfuscated Handshake Protocol Client <-> Client:
27 	- Keycreation:
28 		- Client A (Outgoing connection):
29 				Sendkey:	Md5(<UserHashClientB 16><MagicValue34 1><RandomKeyPartClientA 4>)  21
30 				Receivekey:	Md5(<UserHashClientB 16><MagicValue203 1><RandomKeyPartClientA 4>) 21
31 		- Client B (Incoming connection):
32 				Sendkey:	Md5(<UserHashClientB 16><MagicValue203 1><RandomKeyPartClientA 4>) 21
33 				Receivekey:	Md5(<UserHashClientB 16><MagicValue34 1><RandomKeyPartClientA 4>)  21
34 		NOTE: First 1024 bytes are discarded
35 
36 	- Handshake
37 			-> The handshake is encrypted - except otherwise noted - by the Keys created above
38 			-> Handshake is blocking - do not start sending an answer before the request is completely received (this includes the random bytes)
39 			-> EncryptionMethod = 0 is Obfuscation and the only supported method right now
40 		Client A: <SemiRandomNotProtocolMarker 1[Unencrypted]><RandomKeyPart 4[Unencrypted]><MagicValue 4><EncryptionMethodsSupported 1><EncryptionMethodPreferred 1><PaddingLen 1><RandomBytes PaddingLen%max256>
41 		Client B: <MagicValue 4><EncryptionMethodsSelected 1><PaddingLen 1><RandomBytes PaddingLen%max256>
42 			-> The basic handshake is finished here, if an additional/different EncryptionMethod was selected it may continue negotiating details for this one
43 
44 	- Overhead: 18-48 (~33) Bytes + 2 * IP/TCP Headers per Connection
45 
46 	- Security for Basic Obfuscation:
47 			- Random looking stream, very limited protection against passive eavesdropping single connections
48 
49 	- Additional Comments:
50 			- RandomKeyPart is needed to make multiple connections between two clients look different (but still random), since otherwise the same key
51 			  would be used and RC4 would create the same output. Since the key is a MD5 hash it doesn't weaken the key if that part is known
52 			- Why DH-KeyAgreement isn't used as basic obfuscation key: It doesn't offer substantial more protection against passive connection based protocol identification, it has about 200 bytes more overhead,
53 			  needs more CPU time, we cannot say if the received data is junk, unencrypted or part of the keyagreement before the handshake is finished without losing the complete randomness,
54 			  it doesn't offer substantial protection against eavesdropping without added authentification
55 
56 Basic Obfuscated Handshake Protocol Client <-> Server:
57 	- RC4 Keycreation:
58 		- Client (Outgoing connection):
59 			Sendkey:    Md5(<S 96><MagicValue34 1>)  97
60 			Receivekey: Md5(<S 96><MagicValue203 1>) 97
61 		- Server (Incoming connection):
62 			Sendkey:    Md5(<S 96><MagicValue203 1>) 97
63 			Receivekey: Md5(<S 96><MagicValue34 1>)  97
64 
65 		NOTE: First 1024 Bytes are discarded
66 
67 	- Handshake
68 		-> The handshake is encrypted - except otherwise noted - by the Keys created above
69 		-> Handshake is blocking - do not start sending an answer before the request is completely received (this includes the random bytes)
70 		-> EncryptionMethod = 0 is Obfuscation and the only supported method right now
71 
72 	Client: <SemiRandomNotProtocolMarker 1[Unencrypted]><G^A 96 [Unencrypted]><RandomBytes 0-15 [Unencrypted]>
73 	Server: <G^B 96 [Unencrypted]><MagicValue 4><EncryptionMethodsSupported 1><EncryptionMethodPreferred 1><PaddingLen 1><RandomBytes PaddingLen>
74 	Client: <MagicValue 4><EncryptionMethodsSelected 1><PaddingLen 1><RandomBytes PaddingLen> (Answer delayed till first payload to save a frame)
75 
76 
77 	-> The basic handshake is finished here, if an additional/different EncryptionMethod was selected it may continue negotiating details for this one
78 
79 	- Overhead: 206-251 (~229) Bytes + 2 * IP/TCP Headers Headers per Connection
80 
81 	- DH Agreement Specifics: sizeof(a) and sizeof(b) = 128 Bits, g = 2, p = dh768_p (see below), sizeof p, s, etc. = 768 bits
82 */
83 #include "EncryptedStreamSocket.h"
84 #include "amule.h"
85 #include "Logger.h"
86 #include "Preferences.h"
87 #include "ServerConnect.h"
88 #include "RC4Encrypt.h"
89 #include "MemFile.h"
90 #include "ClientList.h"
91 #include "RandomFunctions.h"
92 
93 #include <algorithm>
94 
95 #include <common/MD5Sum.h>
96 #include <protocol/Protocols.h>
97 
98 #define MAGICVALUE_REQUESTER	34			// modification of the requester-send and server-receive key
99 #define MAGICVALUE_SERVER	203			// modification of the server-send and requester-send key
100 #define MAGICVALUE_SYNC		0x835E6FC4		// value to check if we have a working encrypted stream
101 #define DHAGREEMENT_A_BITS	128
102 
103 #define PRIMESIZE_BYTES	 96
104 static unsigned char dh768_p[] = {
105 	0xF2, 0xBF, 0x52, 0xC5, 0x5F, 0x58, 0x7A, 0xDD, 0x53, 0x71, 0xA9, 0x36,
106 	0xE8, 0x86, 0xEB, 0x3C, 0x62, 0x17, 0xA3, 0x3E, 0xC3, 0x4C, 0xB4, 0x0D,
107 	0xC7, 0x3A, 0x41, 0xA6, 0x43, 0xAF, 0xFC, 0xE7, 0x21, 0xFC, 0x28, 0x63,
108 	0x66, 0x53, 0x5B, 0xDB, 0xCE, 0x25, 0x9F, 0x22, 0x86, 0xDA, 0x4A, 0x91,
109 	0xB2, 0x07, 0xCB, 0xAA, 0x52, 0x55, 0xD4, 0xF6, 0x1C, 0xCE, 0xAE, 0xD4,
110 	0x5A, 0xD5, 0xE0, 0x74, 0x7D, 0xF7, 0x78, 0x18, 0x28, 0x10, 0x5F, 0x34,
111 	0x0F, 0x76, 0x23, 0x87, 0xF8, 0x8B, 0x28, 0x91, 0x42, 0xFB, 0x42, 0x68,
112 	0x8F, 0x05, 0x15, 0x0F, 0x54, 0x8B, 0x5F, 0x43, 0x6A, 0xF7, 0x0D, 0xF3
113 };
114 
115 // winsock2.h already defines it
116 #ifdef SOCKET_ERROR
117 #undef SOCKET_ERROR
118 #endif
119 #define SOCKET_ERROR (-1)
120 
CEncryptedStreamSocket(muleSocketFlags flags,const CProxyData * proxyData)121 CEncryptedStreamSocket::CEncryptedStreamSocket(muleSocketFlags flags, const CProxyData *proxyData) : CSocketClientProxy(flags, proxyData)
122 {
123 	m_StreamCryptState = thePrefs::IsClientCryptLayerSupported() ? ECS_UNKNOWN : ECS_NONE;
124 	m_NegotiatingState = ONS_NONE;
125 	m_nObfusicationBytesReceived = 0;
126 	m_bFullReceive = true;
127 	m_dbgbyEncryptionSupported = 0xFF;
128 	m_dbgbyEncryptionRequested = 0xFF;
129 	m_dbgbyEncryptionMethodSet = 0xFF;
130 	m_nReceiveBytesWanted = 0;
131 	m_EncryptionMethod = ENM_OBFUSCATION;
132 	m_nRandomKeyPart = 0;
133 	m_bServerCrypt = false;
134 }
135 
~CEncryptedStreamSocket()136 CEncryptedStreamSocket::~CEncryptedStreamSocket()
137 {}
138 
139 
140 /* External interface */
141 
SetConnectionEncryption(bool bEnabled,const uint8_t * pTargetClientHash,bool bServerConnection)142 void CEncryptedStreamSocket::SetConnectionEncryption(bool bEnabled, const uint8_t* pTargetClientHash, bool bServerConnection)
143 {
144 	if (m_StreamCryptState != ECS_UNKNOWN && m_StreamCryptState != ECS_NONE) {
145 		if (bEnabled) {
146 			wxFAIL;
147 		}
148 		return;
149 	}
150 
151 	if (bEnabled && pTargetClientHash != NULL && !bServerConnection) {
152 		m_StreamCryptState = ECS_PENDING;
153 		// create obfuscation keys, see on top for key format
154 
155 		// use the crypt random generator
156 		m_nRandomKeyPart = GetRandomUint32();
157 
158 		uint8 achKeyData[21];
159 		md4cpy(achKeyData, pTargetClientHash);
160 		PokeUInt32(achKeyData + 17, m_nRandomKeyPart);
161 
162 		achKeyData[16] = MAGICVALUE_REQUESTER;
163 		MD5Sum md5(achKeyData, sizeof(achKeyData));
164 		m_pfiSendBuffer.SetKey(md5);
165 
166 		achKeyData[16] = MAGICVALUE_SERVER;
167 		md5.Calculate(achKeyData, sizeof(achKeyData));
168 		m_pfiReceiveBuffer.SetKey(md5);
169 
170 	} else if (bServerConnection && bEnabled) {
171 		//printf("->Server crypt\n");
172 		m_bServerCrypt = true;
173 		m_StreamCryptState = ECS_PENDING_SERVER;
174 	} else {
175 		wxASSERT( !bEnabled );
176 		m_StreamCryptState = ECS_NONE;
177 	}
178 }
179 
180 /* Internals, common to base class */
181 
182 // unfortunately sending cannot be made transparent for the derived class, because of WSA_WOULDBLOCK
183 // together with the fact that each byte must pass the keystream only once
Write(const void * lpBuf,uint32_t nBufLen)184 int CEncryptedStreamSocket::Write(const void* lpBuf, uint32_t nBufLen)
185 {
186 	//printf("Starting write for %s\n", (const char*) unicode2char(GetPeer()));
187 	if (!IsEncryptionLayerReady()) {
188 		wxFAIL;
189 		return 0;
190 	} else if (m_bServerCrypt && m_StreamCryptState == ECS_ENCRYPTING && !m_pfiSendBuffer.IsEmpty()) {
191 		wxASSERT( m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING );
192 		// handshakedata was delayed to put it into one frame with the first paypload to the server
193 		// do so now with the payload attached
194 		int nRes = SendNegotiatingData(lpBuf, nBufLen, nBufLen);
195 		wxASSERT( nRes != SOCKET_ERROR );
196 		(void)nRes;
197 		return nBufLen;	// report a full send, even if we didn't for some reason - the data is now in our buffer and will be handled later
198 	} else if (m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING) {
199 		wxFAIL;
200 	}
201 
202 	if (m_StreamCryptState == ECS_UNKNOWN) {
203 		//this happens when the encryption option was not set on an outgoing connection
204 		//or if we try to send before receiving on an incoming connection - both shouldn't happen
205 		m_StreamCryptState = ECS_NONE;
206 		//DebugLogError(_T("CEncryptedStreamSocket: Overwriting State ECS_UNKNOWN with ECS_NONE because of premature Send() (%s)"), GetPeer());
207 	}
208 
209 	//printf("Writing %i bytes of data\n", nBufLen);
210 	return CSocketClientProxy::Write(lpBuf, nBufLen);
211 }
212 
Read(void * lpBuf,uint32_t nBufLen)213 int CEncryptedStreamSocket::Read(void* lpBuf, uint32_t nBufLen)
214 {
215 	m_nObfusicationBytesReceived = CSocketClientProxy::Read(lpBuf, nBufLen);
216 	m_bFullReceive = m_nObfusicationBytesReceived == (uint32)nBufLen;
217 
218 	//printf("Read %i bytes on %s, socket %p\n", m_nObfusicationBytesReceived, (const char*) unicode2char(GetPeer()), this);
219 
220 	if (m_nObfusicationBytesReceived == (uint32_t)SOCKET_ERROR || m_nObfusicationBytesReceived <= 0) {
221 		return m_nObfusicationBytesReceived;
222 	}
223 
224 	switch (m_StreamCryptState) {
225 		case ECS_NONE: // disabled, just pass it through
226 			return m_nObfusicationBytesReceived;
227 		case ECS_PENDING:
228 		case ECS_PENDING_SERVER:
229 			//printf("Received %i bytes before sending?\n", m_nObfusicationBytesReceived);
230 			wxFAIL;
231 			//DebugLogError(_T("CEncryptedStreamSocket Received data before sending on outgoing connection"));
232 			m_StreamCryptState = ECS_NONE;
233 			return m_nObfusicationBytesReceived;
234 		case ECS_UNKNOWN: {
235 			//printf("Receiving encrypted data on ECS_UNKNOWN\n");
236 			uint32_t nRead = 1;
237 			bool bNormalHeader = false;
238 			switch (((uint8_t*)lpBuf)[0]) {
239 				case OP_EDONKEYPROT:
240 				case OP_PACKEDPROT:
241 				case OP_EMULEPROT:
242 					bNormalHeader = true;
243 					break;
244 			}
245 
246 			if (!bNormalHeader) {
247 				//printf("Not a normal header, negotiating encryption\n");
248 				StartNegotiation(false);
249 				const uint32 nNegRes = Negotiate((uint8_t*)lpBuf + nRead, m_nObfusicationBytesReceived - nRead);
250 				if (nNegRes == (uint32_t)(-1)) {
251 					return 0;
252 				}
253 				nRead += nNegRes;
254 				if (nRead != (uint32_t)m_nObfusicationBytesReceived) {
255 					// this means we have more data then the current negotiation step required (or there is a bug) and this should never happen
256 					// (note: even if it just finished the handshake here, there still can be no data left, since the other client didn't receive our response yet)
257 					//DebugLogError(_T("CEncryptedStreamSocket: Client %s sent more data then expected while negotiating, disconnecting (1)"), GetPeer());
258 					//printf("On error: encryption\n");
259 					OnError(ERR_ENCRYPTION);
260 				}
261 				return 0;
262 			} else {
263 				// doesn't seem to be encrypted
264 				//printf("Encrypted data doesn't seem to be encrypted\n");
265 				m_StreamCryptState = ECS_NONE;
266 
267 				// if we require an encrypted connection, cut the connection here. This shouldn't happen that often
268 				// at least with other up-to-date eMule clients because they check for incompability before connecting if possible
269 				if (thePrefs::IsClientCryptLayerRequired()) {
270 					// TODO: Remove me when I have been solved
271 					// Even if the Require option is enabled, we currently have to accept unencrypted connection which are made
272 					// for lowid/firewall checks from servers and other from us selected client. Otherwise, this option would
273 					// always result in a lowid/firewalled status. This is of course not nice, but we can't avoid this workaround
274 					// until servers and kad completely support encryption too, which will at least for kad take a bit
275 					// only exception is the .ini option ClientCryptLayerRequiredStrict which will even ignore test connections
276 					// Update: New server now support encrypted callbacks
277 					uint32_t ip = GetPeerInt();
278 					if (thePrefs::IsClientCryptLayerRequiredStrict() || (!theApp->serverconnect->AwaitingTestFromIP(ip)
279 						&& !theApp->clientlist->IsKadFirewallCheckIP(ip)) )
280 					{
281 						OnError(ERR_ENCRYPTION_NOTALLOWED);
282 						return 0;
283 					} else {
284 						//AddDebugLogLine(DLP_DEFAULT, false, _T("Incoming unencrypted firewallcheck connection permitted despite RequireEncryption setting  - %s"), GetPeer() );
285 					}
286 				}
287 				return m_nObfusicationBytesReceived; // buffer was unchanged, we can just pass it through
288 			}
289 		}
290 		case ECS_ENCRYPTING:
291 			//printf("Encryption enabled on data receiving, decrypting and passing along\n");
292 			// basic obfusication enabled and set, so decrypt and pass along
293 			m_pfiReceiveBuffer.RC4Crypt((uint8_t*)lpBuf, (uint8_t*)lpBuf, m_nObfusicationBytesReceived);
294 			//DumpMem(lpBuf, m_nObfusicationBytesReceived, wxT("Directly decrypted data:"));
295 			return m_nObfusicationBytesReceived;
296 		case ECS_NEGOTIATING:{
297 			//printf("Negotiating on data receive\n");
298 			const uint32_t nRead = Negotiate((uint8_t*)lpBuf, m_nObfusicationBytesReceived);
299 			if (nRead == (uint32_t)(-1)) {
300 				//printf("-> Encryption read error on negotiation\n");
301 				return 0;
302 			} else if (nRead != (uint32_t)m_nObfusicationBytesReceived && m_StreamCryptState != ECS_ENCRYPTING) {
303 				//printf("-> Too much data, bailing out of negotiation step\n");
304 				// this means we have more data then the current negotiation step required (or there is a bug) and this should never happen
305 				//DebugLogError(_T("CEncryptedStreamSocket: Client %s sent more data then expected while negotiating, disconnecting (2)"), GetPeer());
306 				OnError(ERR_ENCRYPTION);
307 				return 0;
308 			} else if (nRead != (uint32_t)m_nObfusicationBytesReceived && m_StreamCryptState == ECS_ENCRYPTING) {
309 				//printf("-> Handshake negotiation finished\n");
310 				// we finished the handshake and if we this was an outgoing connection it is allowed (but strange and unlikely) that the client sent payload
311 				//DebugLogWarning(_T("CEncryptedStreamSocket: Client %s has finished the handshake but also sent payload on a outgoing connection"), GetPeer());
312 				memmove(lpBuf, (uint8_t*)lpBuf + nRead, m_nObfusicationBytesReceived - nRead);
313 				return m_nObfusicationBytesReceived - nRead;
314 			} else {
315 				//printf("-> Negotiation went probably ok\n");
316 				return 0;
317 			}
318 		}
319 		default:
320 			wxFAIL;
321 			return m_nObfusicationBytesReceived;
322 	}
323 }
324 
OnSend(int)325 void CEncryptedStreamSocket::OnSend(int)
326 {
327 	// if the socket just connected and this is outgoing, we might want to start the handshake here
328 	if (m_StreamCryptState == ECS_PENDING || m_StreamCryptState == ECS_PENDING_SERVER){
329 		//printf("Starting connection negotiation on OnSend for %s\n", (const char*) unicode2char(GetPeer()));
330 		StartNegotiation(true);
331 		return;
332 	}
333 
334 	// check if we have negotiating data pending
335 	if (!m_pfiSendBuffer.IsEmpty()) {
336 		wxASSERT( m_StreamCryptState >= ECS_NEGOTIATING );
337 		SendNegotiatingData(NULL, 0);
338 	}
339 }
340 
CryptPrepareSendData(uint8 * pBuffer,uint32 nLen)341 void CEncryptedStreamSocket::CryptPrepareSendData(uint8* pBuffer, uint32 nLen)
342 {
343 	if (!IsEncryptionLayerReady()) {
344 		wxFAIL; // must be a bug
345 		return;
346 	}
347 	if (m_StreamCryptState == ECS_UNKNOWN) {
348 		//this happens when the encryption option was not set on an outgoing connection
349 		//or if we try to send before receiving on an incoming connection - both shouldn't happen
350 		m_StreamCryptState = ECS_NONE;
351 		//DebugLogError(_T("CEncryptedStreamSocket: Overwriting State ECS_UNKNOWN with ECS_NONE because of premature Send() (%s)"), GetPeer());
352 	}
353 	if (m_StreamCryptState == ECS_ENCRYPTING) {
354 		//printf("Preparing crypt data on %s\n", (const char*) unicode2char(GetPeer()));
355 		//DumpMem(pBuffer, nLen, wxT("Before crypt prepare:\n"));
356 		m_pfiSendBuffer.RC4Crypt(pBuffer, pBuffer, nLen);
357 		//DumpMem(pBuffer, nLen, wxT("After crypt prepare:\n"));
358 	}
359 }
360 
361 /* Internals, just for this class (can be raped) */
362 
IsEncryptionLayerReady()363 bool CEncryptedStreamSocket::IsEncryptionLayerReady()
364 {
365 	return ( (m_StreamCryptState == ECS_NONE || m_StreamCryptState == ECS_ENCRYPTING || m_StreamCryptState == ECS_UNKNOWN )
366 		&& (m_pfiSendBuffer.IsEmpty() || (m_bServerCrypt && m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING)) );
367 }
368 
StartNegotiation(bool bOutgoing)369 void CEncryptedStreamSocket::StartNegotiation(bool bOutgoing)
370 {
371 	//printf("Starting socket negotiation\n");
372 	if (!bOutgoing) {
373 		//printf("Incoming connection negotiation on %s\n", (const char*) unicode2char(GetPeer()));
374 		m_NegotiatingState = ONS_BASIC_CLIENTA_RANDOMPART;
375 		m_StreamCryptState = ECS_NEGOTIATING;
376 		m_nReceiveBytesWanted = 4;
377 	} else if (m_StreamCryptState == ECS_PENDING) {
378 		//printf("Socket is client.pending on negotiation\n");
379 		CMemFile fileRequest(29);
380 		const uint8_t bySemiRandomNotProtocolMarker = GetSemiRandomNotProtocolMarker();
381 		fileRequest.WriteUInt8(bySemiRandomNotProtocolMarker);
382 		fileRequest.WriteUInt32(m_nRandomKeyPart);
383 		fileRequest.WriteUInt32(MAGICVALUE_SYNC);
384 		const uint8_t bySupportedEncryptionMethod = ENM_OBFUSCATION; // we do not support any further encryption in this version
385 		fileRequest.WriteUInt8(bySupportedEncryptionMethod);
386 		fileRequest.WriteUInt8(bySupportedEncryptionMethod); // so we also prefer this one
387 		uint8_t byPadding = (uint8_t)(GetRandomUint8() % (thePrefs::GetCryptTCPPaddingLength() + 1));
388 		fileRequest.WriteUInt8(byPadding);
389 		for (int i = 0; i < byPadding; i++) {
390 			fileRequest.WriteUInt8(GetRandomUint8());
391 		}
392 
393 		m_NegotiatingState = ONS_BASIC_CLIENTB_MAGICVALUE;
394 		m_StreamCryptState = ECS_NEGOTIATING;
395 		m_nReceiveBytesWanted = 4;
396 
397 		SendNegotiatingData(fileRequest.GetRawBuffer(), (uint32_t)fileRequest.GetLength(), 5);
398 	} else if (m_StreamCryptState == ECS_PENDING_SERVER) {
399 		//printf("Socket is server.pending on negotiation\n");
400 		CMemFile fileRequest(113);
401 		const uint8_t bySemiRandomNotProtocolMarker = GetSemiRandomNotProtocolMarker();
402 		fileRequest.WriteUInt8(bySemiRandomNotProtocolMarker);
403 
404 		m_cryptDHA.Randomize((CryptoPP::AutoSeededRandomPool&)GetRandomPool(), DHAGREEMENT_A_BITS); // our random a
405 		wxASSERT( m_cryptDHA.MinEncodedSize() <= DHAGREEMENT_A_BITS / 8 );
406 		CryptoPP::Integer cryptDHPrime((uint8_t*)dh768_p, PRIMESIZE_BYTES);  // our fixed prime
407 		// calculate g^a % p
408 		CryptoPP::Integer cryptDHGexpAmodP = a_exp_b_mod_c(CryptoPP::Integer(2), m_cryptDHA, cryptDHPrime);
409 		wxASSERT( m_cryptDHA.MinEncodedSize() <= PRIMESIZE_BYTES );
410 		// put the result into a buffer
411 		uint8_t aBuffer[PRIMESIZE_BYTES];
412 		cryptDHGexpAmodP.Encode(aBuffer, PRIMESIZE_BYTES);
413 
414 		fileRequest.Write(aBuffer, PRIMESIZE_BYTES);
415 		uint8 byPadding = (uint8)(GetRandomUint8() % 16); // add random padding
416 		fileRequest.WriteUInt8(byPadding);
417 
418 		for (int i = 0; i < byPadding; i++) {
419 			fileRequest.WriteUInt8(GetRandomUint8());
420 		}
421 
422 		m_NegotiatingState = ONS_BASIC_SERVER_DHANSWER;
423 		m_StreamCryptState = ECS_NEGOTIATING;
424 		m_nReceiveBytesWanted = 96;
425 
426 		SendNegotiatingData(fileRequest.GetRawBuffer(), (uint32_t)fileRequest.GetLength(), (uint32_t)fileRequest.GetLength());
427 	} else {
428 		wxFAIL;
429 		m_StreamCryptState = ECS_NONE;
430 		return;
431 	}
432 }
433 
Negotiate(const uint8 * pBuffer,uint32 nLen)434 int CEncryptedStreamSocket::Negotiate(const uint8* pBuffer, uint32 nLen)
435 {
436 	uint32_t nRead = 0;
437 	wxASSERT( m_nReceiveBytesWanted > 0 );
438 
439 	//DumpMem(pBuffer, nLen, wxT("Negotiate buffer: "));
440 
441 	try {
442 		while (m_NegotiatingState != ONS_COMPLETE && m_nReceiveBytesWanted > 0) {
443 			if (m_nReceiveBytesWanted > 512) {
444 				wxFAIL;
445 				return 0;
446 			}
447 
448 			const uint32_t nToRead =  std::min(nLen - nRead, m_nReceiveBytesWanted);
449 			//printf("Reading %i bytes, add from %i position on %i position\n",nToRead, nRead, (int)m_pfiReceiveBuffer.GetPosition());
450 			//DumpMem(pBuffer + nRead, nToRead, wxT("Recv Buffer: "));
451 			m_pfiReceiveBuffer.Write(pBuffer + nRead, nToRead);
452 			nRead += nToRead;
453 			m_nReceiveBytesWanted -= nToRead;
454 			if (m_nReceiveBytesWanted > 0)  {
455 				return nRead;
456 			}
457 
458 			if (m_NegotiatingState != ONS_BASIC_CLIENTA_RANDOMPART && m_NegotiatingState != ONS_BASIC_SERVER_DHANSWER) {
459 				// We have the keys, decrypt
460 				//printf("We have the keys, so decrypt away on %s\n", (const char*) unicode2char(GetPeer()));
461 				m_pfiReceiveBuffer.Encrypt();
462 			}
463 
464 			m_pfiReceiveBuffer.Seek(0);
465 
466 			switch (m_NegotiatingState) {
467 				case ONS_NONE: // would be a bug
468 					wxFAIL;
469 					return 0;
470 				case ONS_BASIC_CLIENTA_RANDOMPART: {
471 					//printf("We are on ONS_BASIC_CLIENTA_RANDOMPART, create the keys on %s\n", (const char*) unicode2char(GetPeer()));
472 					// This creates the send/receive keys.
473 
474 					uint8_t achKeyData[21];
475 					md4cpy(achKeyData, thePrefs::GetUserHash().GetHash());
476 					m_pfiReceiveBuffer.Read(achKeyData + 17, 4);
477 
478 					achKeyData[16] = MAGICVALUE_REQUESTER;
479 
480 					//DumpMem(achKeyData, sizeof(achKeyData), wxT("ach:"));
481 
482 					MD5Sum md5(achKeyData, sizeof(achKeyData));
483 					//DumpMem(md5.GetRawHash(), 16, wxT("Md5:"));
484 					m_pfiReceiveBuffer.SetKey(md5);
485 
486 					achKeyData[16] = MAGICVALUE_SERVER;
487 					md5.Calculate(achKeyData, sizeof(achKeyData));
488 					m_pfiSendBuffer.SetKey(md5);
489 
490 					m_NegotiatingState = ONS_BASIC_CLIENTA_MAGICVALUE;
491 					m_nReceiveBytesWanted = 4;
492 					break;
493 				}
494 				case ONS_BASIC_CLIENTA_MAGICVALUE: {
495 					// Check the magic value to confirm encryption works.
496 					//printf("Creating magic value on negotiate on %s\n", (const char*) unicode2char(GetPeer()));
497 
498 					uint32_t dwValue = m_pfiReceiveBuffer.ReadUInt32();
499 
500 					if (dwValue == MAGICVALUE_SYNC) {
501 						// yup, the one or the other way it worked, this is an encrypted stream
502 						//DEBUG_ONLY( DebugLog(_T("Received proper magic value, clientIP: %s"), GetPeer()) );
503 						// set the receiver key
504 						//printf("Magic value works on %s\n", (const char*) unicode2char(GetPeer()));
505 						m_NegotiatingState = ONS_BASIC_CLIENTA_METHODTAGSPADLEN;
506 						m_nReceiveBytesWanted = 3;
507 					} else {
508 						//printf("Wrong magic value: 0x%x != 0x%x on %s\n",dwValue, MAGICVALUE_SYNC, (const char*)unicode2char(GetPeer()));
509 						//DebugLogError(_T("CEncryptedStreamSocket: Received wrong magic value from clientIP %s on a supposly encrytped stream / Wrong Header"), GetPeer());
510 						OnError(ERR_ENCRYPTION);
511 						return (-1);
512 					}
513 					break;
514 				}
515 				case ONS_BASIC_CLIENTA_METHODTAGSPADLEN: {
516 					// Get encryption method and padding.
517 					// Might fall back to padding process, but the bytes will be ignored.
518 					//printf("Getting encryption method on negotiation\n");
519 
520 					m_dbgbyEncryptionSupported = m_pfiReceiveBuffer.ReadUInt8();
521 					m_dbgbyEncryptionRequested = m_pfiReceiveBuffer.ReadUInt8();
522 
523 					if (m_dbgbyEncryptionRequested != ENM_OBFUSCATION) {
524 						//printf("Unsupported encryption method!\n");
525 //						AddDebugLogLine(DLP_LOW, false, _T("CEncryptedStreamSocket: Client %s preffered unsupported encryption method (%i)"), GetPeer(), m_dbgbyEncryptionRequested);
526 					}
527 
528 					m_nReceiveBytesWanted = m_pfiReceiveBuffer.ReadUInt8();
529 					m_NegotiatingState = ONS_BASIC_CLIENTA_PADDING;
530 
531 					if (m_nReceiveBytesWanted > 0) {
532 						// No padding
533 						break;
534 					}
535 				}
536 				/* fall through */
537 				case ONS_BASIC_CLIENTA_PADDING: {
538 					//printf("Negotiating on padding, completing\n");
539 					// ignore the random bytes, send the response, set status complete
540 					CMemFile fileResponse(26);
541 					fileResponse.WriteUInt32(MAGICVALUE_SYNC);
542 					const uint8_t bySelectedEncryptionMethod = ENM_OBFUSCATION; // we do not support any further encryption in this version, so no need to look which the other client preferred
543 					fileResponse.WriteUInt8(bySelectedEncryptionMethod);
544 
545 					const uint8_t byPaddingLen = theApp->serverconnect->AwaitingTestFromIP(GetPeerInt()) ? 16 : (thePrefs::GetCryptTCPPaddingLength() + 1);
546 					uint8_t byPadding = (uint8_t)(GetRandomUint8() % byPaddingLen);
547 
548 					fileResponse.WriteUInt8(byPadding);
549 					for (int i = 0; i < byPadding; i++) {
550 						fileResponse.WriteUInt8((uint8_t)rand());
551 					}
552 					SendNegotiatingData(fileResponse.GetRawBuffer(), (uint32_t)fileResponse.GetLength());
553 					m_NegotiatingState = ONS_COMPLETE;
554 					m_StreamCryptState = ECS_ENCRYPTING;
555 					//DEBUG_ONLY( DebugLog(_T("CEncryptedStreamSocket: Finished Obufscation handshake with client %s (incoming)"), GetPeer()) );
556 					break;
557 				}
558 				case ONS_BASIC_CLIENTB_MAGICVALUE: {
559 					//printf("Negotiating on magic value\n");
560 					if (m_pfiReceiveBuffer.ReadUInt32() != MAGICVALUE_SYNC) {
561 						//DebugLogError(_T("CEncryptedStreamSocket: EncryptedstreamSyncError: Client sent wrong Magic Value as answer, cannot complete handshake (%s)"), GetPeer());
562 						OnError(ERR_ENCRYPTION);
563 						return (-1);
564 					}
565 					m_NegotiatingState = ONS_BASIC_CLIENTB_METHODTAGSPADLEN;
566 					m_nReceiveBytesWanted = 2;
567 					break;
568 				}
569 				case ONS_BASIC_CLIENTB_METHODTAGSPADLEN: {
570 					//printf("Negotiating on client B pad length\n");
571 					m_dbgbyEncryptionMethodSet = m_pfiReceiveBuffer.ReadUInt8();
572 					if (m_dbgbyEncryptionMethodSet != ENM_OBFUSCATION) {
573 						//DebugLogError( _T("CEncryptedStreamSocket: Client %s set unsupported encryption method (%i), handshake failed"), GetPeer(), m_dbgbyEncryptionMethodSet);
574 						OnError(ERR_ENCRYPTION);
575 						return (-1);
576 					}
577 					m_nReceiveBytesWanted = m_pfiReceiveBuffer.ReadUInt8();
578 					m_NegotiatingState = ONS_BASIC_CLIENTB_PADDING;
579 					if (m_nReceiveBytesWanted > 0) {
580 						break;
581 					}
582 				}
583 				/* fall through */
584 				case ONS_BASIC_CLIENTB_PADDING:
585 					//printf("Negotiating on client B padding, handshake complete\n");
586 					// ignore the random bytes, the handshake is complete
587 					m_NegotiatingState = ONS_COMPLETE;
588 					m_StreamCryptState = ECS_ENCRYPTING;
589 					//DEBUG_ONLY( DebugLog(_T("CEncryptedStreamSocket: Finished Obufscation handshake with client %s (outgoing)"), GetPeer()) );
590 					break;
591 				case ONS_BASIC_SERVER_DHANSWER: {
592 					wxASSERT( !m_cryptDHA.IsZero() );
593 					uint8_t aBuffer[PRIMESIZE_BYTES + 1];
594 					m_pfiReceiveBuffer.Read(aBuffer, PRIMESIZE_BYTES);
595 					CryptoPP::Integer cryptDHAnswer((uint8_t*)aBuffer, PRIMESIZE_BYTES);
596 					CryptoPP::Integer cryptDHPrime((uint8_t*)dh768_p, PRIMESIZE_BYTES);  // our fixed prime
597 					CryptoPP::Integer cryptResult = a_exp_b_mod_c(cryptDHAnswer, m_cryptDHA, cryptDHPrime);
598 
599 					m_cryptDHA = 0;
600 					//DEBUG_ONLY( ZeroMemory(aBuffer, sizeof(aBuffer)) );
601 					wxASSERT( cryptResult.MinEncodedSize() <= PRIMESIZE_BYTES );
602 
603 					// create the keys
604 					cryptResult.Encode(aBuffer, PRIMESIZE_BYTES);
605 					aBuffer[PRIMESIZE_BYTES] = MAGICVALUE_REQUESTER;
606 					MD5Sum md5(aBuffer, sizeof(aBuffer));
607 					m_pfiSendBuffer.SetKey(md5);
608 					aBuffer[PRIMESIZE_BYTES] = MAGICVALUE_SERVER;
609 					md5.Calculate(aBuffer, sizeof(aBuffer));
610 					m_pfiReceiveBuffer.SetKey(md5);
611 
612 					m_NegotiatingState = ONS_BASIC_SERVER_MAGICVALUE;
613 					m_nReceiveBytesWanted = 4;
614 					break;
615 				}
616 				case ONS_BASIC_SERVER_MAGICVALUE: {
617 					uint32_t dwValue = m_pfiReceiveBuffer.ReadUInt32();
618 					if (dwValue == MAGICVALUE_SYNC) {
619 						// yup, the one or the other way it worked, this is an encrypted stream
620 						//DebugLog(_T("Received proper magic value after DH-Agreement from Serverconnection IP: %s"), GetPeer());
621 						// set the receiver key
622 						m_NegotiatingState = ONS_BASIC_SERVER_METHODTAGSPADLEN;
623 						m_nReceiveBytesWanted = 3;
624 					} else {
625 						//DebugLogError(_T("CEncryptedStreamSocket: Received wrong magic value after DH-Agreement from Serverconnection"), GetPeer());
626 						OnError(ERR_ENCRYPTION);
627 						return (-1);
628 					}
629 					break;
630 				}
631 				case ONS_BASIC_SERVER_METHODTAGSPADLEN:
632 					m_dbgbyEncryptionSupported = m_pfiReceiveBuffer.ReadUInt8();
633 					m_dbgbyEncryptionRequested = m_pfiReceiveBuffer.ReadUInt8();
634 					if (m_dbgbyEncryptionRequested != ENM_OBFUSCATION) {
635 	//					AddDebugLogLine(DLP_LOW, false, _T("CEncryptedStreamSocket: Server %s preffered unsupported encryption method (%i)"), GetPeer(), m_dbgbyEncryptionRequested);
636 					}
637 					m_nReceiveBytesWanted = m_pfiReceiveBuffer.ReadUInt8();
638 					m_NegotiatingState = ONS_BASIC_SERVER_PADDING;
639 					if (m_nReceiveBytesWanted > 0) {
640 						break;
641 					}
642 				/* fall through */
643 				case ONS_BASIC_SERVER_PADDING: {
644 					// ignore the random bytes (they are decrypted already), send the response, set status complete
645 					CMemFile fileResponse(26);
646 					fileResponse.WriteUInt32(MAGICVALUE_SYNC);
647 					const uint8_t bySelectedEncryptionMethod = ENM_OBFUSCATION; // we do not support any further encryption in this version, so no need to look which the other client preferred
648 					fileResponse.WriteUInt8(bySelectedEncryptionMethod);
649 
650 					// Server callback connection only allows 16 bytes of padding.
651 					uint8_t byPadding = (uint8_t)(GetRandomUint8() % 16);
652 					fileResponse.WriteUInt8(byPadding);
653 
654 					for (int i = 0; i < byPadding; i++) {
655 						fileResponse.WriteUInt8((uint8_t)rand());
656 					}
657 
658 					m_NegotiatingState = ONS_BASIC_SERVER_DELAYEDSENDING;
659 					SendNegotiatingData(fileResponse.GetRawBuffer(), (uint32_t)fileResponse.GetLength(), 0, true); // don't actually send it right now, store it in our sendbuffer
660 					m_StreamCryptState = ECS_ENCRYPTING;
661 					//DEBUG_ONLY( DebugLog(_T("CEncryptedStreamSocket: Finished DH Obufscation handshake with Server %s"), GetPeer()) );
662 					break;
663 				}
664 				default:
665 					wxFAIL;
666 			}
667 			m_pfiReceiveBuffer.ResetData();
668 		}
669 		return nRead;
670 	} catch(...) {
671 		// can only be caused by a bug in negationhandling, not by the datastream
672 		//error->Delete();
673 		//printf("Bug on negotiation?\n");
674 		wxFAIL;
675 		OnError(ERR_ENCRYPTION);
676 		m_pfiReceiveBuffer.ResetData();
677 		return (-1);
678 	}
679 }
680 
SendNegotiatingData(const void * lpBuf,uint32_t nBufLen,uint32_t nStartCryptFromByte,bool bDelaySend)681 int CEncryptedStreamSocket::SendNegotiatingData(const void* lpBuf, uint32_t nBufLen, uint32_t nStartCryptFromByte, bool bDelaySend)
682 {
683 	wxASSERT( m_StreamCryptState == ECS_NEGOTIATING || m_StreamCryptState == ECS_ENCRYPTING );
684 	wxASSERT( nStartCryptFromByte <= nBufLen );
685 	wxASSERT( m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING || !bDelaySend );
686 	//printf("Send negotiation data on %s\n", (const char*) unicode2char(GetPeer()));
687 	uint8_t* pBuffer = NULL;
688 	bool bProcess = false;
689 	if (lpBuf != NULL) {
690 		pBuffer = new uint8_t[nBufLen];
691 		if (pBuffer == NULL) {
692 			throw CMuleException(wxT("Memory exception"), wxT("Memory exception on TCP encrypted socket"));
693 		}
694 
695 		if (nStartCryptFromByte > 0) {
696 			memcpy(pBuffer, lpBuf, nStartCryptFromByte);
697 		}
698 
699 		if (nBufLen - nStartCryptFromByte > 0) {
700 			//printf("Crypting negotiation data on %s starting on byte %i\n", (const char*) unicode2char(GetPeer()), nStartCryptFromByte);
701 			//DumpMem(lpBuf, nBufLen, wxT("Pre-encryption:"));
702 			m_pfiSendBuffer.RC4Crypt((uint8*)lpBuf + nStartCryptFromByte, pBuffer + nStartCryptFromByte, nBufLen - nStartCryptFromByte);
703 			//DumpMem(pBuffer, nBufLen, wxT("Post-encryption:"));
704 		}
705 
706 		if (!m_pfiSendBuffer.IsEmpty()) {
707 			// we already have data pending. Attach it and try to send
708 			if (m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING) {
709 				m_NegotiatingState = ONS_COMPLETE;
710 			} else {
711 				wxFAIL;
712 			}
713 			m_pfiSendBuffer.Append(pBuffer, nBufLen);
714 			delete[] pBuffer;
715 			pBuffer = NULL;
716 			nStartCryptFromByte = 0;
717 			bProcess = true; // we want to try to send it right now
718 		}
719 	}
720 
721 	if (lpBuf == NULL || bProcess) {
722 		// this call is for processing pending data
723 		if (m_pfiSendBuffer.IsEmpty() || nStartCryptFromByte != 0) {
724 			wxFAIL;
725 			return 0;							// or not
726 		}
727 		nBufLen = (uint32)m_pfiSendBuffer.GetLength();
728 		pBuffer = m_pfiSendBuffer.Detach();
729 	}
730 
731 	wxASSERT( m_pfiSendBuffer.IsEmpty() );
732 
733 	uint32_t result = 0;
734 	if (!bDelaySend) {
735 		//printf("Writing negotiation data on %s: ", (const char*) unicode2char(GetPeer()));
736 		result = CSocketClientProxy::Write(pBuffer, nBufLen);
737 		//printf("Wrote %i bytes\n",result);
738 	}
739 
740 	if (result == (uint32_t)SOCKET_ERROR || bDelaySend) {
741 		m_pfiSendBuffer.Write(pBuffer, nBufLen);
742 		delete[] pBuffer;
743 		return result;
744 	} else {
745 		if (result < nBufLen) {
746 			// Store the partial data pending
747 			//printf("Partial negotiation pending on %s\n", (const char*) unicode2char(GetPeer()));
748 			m_pfiSendBuffer.Write(pBuffer + result, nBufLen - result);
749 		}
750 		delete[] pBuffer;
751 		return result;
752 	}
753 }
754 
755 
GetSemiRandomNotProtocolMarker() const756 uint8_t CEncryptedStreamSocket::GetSemiRandomNotProtocolMarker() const
757 {
758 	uint8_t bySemiRandomNotProtocolMarker = 0;
759 	bool bOk = false;
760 	for (int i = 0; i < 128; i++) {
761 		bySemiRandomNotProtocolMarker = GetRandomUint8();
762 		switch (bySemiRandomNotProtocolMarker) { // not allowed values
763 				case OP_EDONKEYPROT:
764 				case OP_PACKEDPROT:
765 				case OP_EMULEPROT:
766 					break;
767 				default:
768 					bOk = true;
769 		}
770 
771 		if (bOk) {
772 			break;
773 		}
774 	}
775 
776 	if (!bOk) {
777 		// either we have _real_ bad luck or the randomgenerator is a bit messed up
778 		wxFAIL;
779 		bySemiRandomNotProtocolMarker = 0x01;
780 	}
781 	return bySemiRandomNotProtocolMarker;
782 }
783