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
56Basic 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
104static 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
121CEncryptedStreamSocket::CEncryptedStreamSocket(wxSocketFlags 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
136CEncryptedStreamSocket::~CEncryptedStreamSocket()
137{}
138
139
140/* External interface */
141
142void CEncryptedStreamSocket::SetConnectionEncryption(bool bEnabled, const uint8_t* pTargetClientHash, bool bServerConnection)
143{
144	if (m_StreamCryptState != ECS_UNKNOWN && m_StreamCryptState != ECS_NONE) {
145		if (!m_StreamCryptState == ECS_NONE || 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
184int CEncryptedStreamSocket::Write(const void* lpBuf, uint32_t nBufLen)
185{
186	//printf("Starting write for %s\n", (const char*) unicode2char(DbgGetIPString()));
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)"), DbgGetIPString());
207	}
208
209	//printf("Writing %i bytes of data\n", nBufLen);
210	CSocketClientProxy::Write(lpBuf, nBufLen);
211	return CSocketClientProxy::LastCount();
212}
213
214int CEncryptedStreamSocket::Read(void* lpBuf, uint32_t nBufLen)
215{
216	CSocketClientProxy::Read(lpBuf, nBufLen);
217	m_nObfusicationBytesReceived = CSocketClientProxy::LastCount();
218	m_bFullReceive = m_nObfusicationBytesReceived == (uint32)nBufLen;
219
220	//printf("Read %i bytes on %s, socket %p\n", m_nObfusicationBytesReceived, (const char*) unicode2char(DbgGetIPString()), this);
221
222	if (m_nObfusicationBytesReceived == (uint32_t)SOCKET_ERROR || m_nObfusicationBytesReceived <= 0) {
223		return m_nObfusicationBytesReceived;
224	}
225
226	switch (m_StreamCryptState) {
227		case ECS_NONE: // disabled, just pass it through
228			return m_nObfusicationBytesReceived;
229		case ECS_PENDING:
230		case ECS_PENDING_SERVER:
231			//printf("Received %i bytes before sending?\n", m_nObfusicationBytesReceived);
232			wxFAIL;
233			//DebugLogError(_T("CEncryptedStreamSocket Received data before sending on outgoing connection"));
234			m_StreamCryptState = ECS_NONE;
235			return m_nObfusicationBytesReceived;
236		case ECS_UNKNOWN: {
237			//printf("Receiving encrypted data on ECS_UNKNOWN\n");
238			uint32_t nRead = 1;
239			bool bNormalHeader = false;
240			switch (((uint8_t*)lpBuf)[0]) {
241				case OP_EDONKEYPROT:
242				case OP_PACKEDPROT:
243				case OP_EMULEPROT:
244					bNormalHeader = true;
245					break;
246			}
247
248			if (!bNormalHeader) {
249				//printf("Not a normal header, negotiating encryption\n");
250				StartNegotiation(false);
251				const uint32 nNegRes = Negotiate((uint8_t*)lpBuf + nRead, m_nObfusicationBytesReceived - nRead);
252				if (nNegRes == (uint32_t)(-1)) {
253					return 0;
254				}
255				nRead += nNegRes;
256				if (nRead != (uint32_t)m_nObfusicationBytesReceived) {
257					// this means we have more data then the current negotiation step required (or there is a bug) and this should never happen
258					// (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)
259					//DebugLogError(_T("CEncryptedStreamSocket: Client %s sent more data then expected while negotiating, disconnecting (1)"), DbgGetIPString());
260					//printf("On error: encryption\n");
261					OnError(ERR_ENCRYPTION);
262				}
263				return 0;
264			} else {
265				// doesn't seem to be encrypted
266				//printf("Encrypted data doesn't seem to be encrypted\n");
267				m_StreamCryptState = ECS_NONE;
268
269				// if we require an encrypted connection, cut the connection here. This shouldn't happen that often
270				// at least with other up-to-date eMule clients because they check for incompability before connecting if possible
271				if (thePrefs::IsClientCryptLayerRequired()) {
272					// TODO: Remove me when I have been solved
273					// Even if the Require option is enabled, we currently have to accept unencrypted connection which are made
274					// for lowid/firewall checks from servers and other from us selected client. Otherwise, this option would
275					// always result in a lowid/firewalled status. This is of course not nice, but we can't avoid this workaround
276					// until servers and kad completely support encryption too, which will at least for kad take a bit
277					// only exception is the .ini option ClientCryptLayerRequiredStrict which will even ignore test connections
278					// Update: New server now support encrypted callbacks
279					amuleIPV4Address address;
280					GetPeer(address);
281					uint32_t ip = StringIPtoUint32(address.IPAddress());
282					if (thePrefs::IsClientCryptLayerRequiredStrict() || (!theApp->serverconnect->AwaitingTestFromIP(ip)
283						&& !theApp->clientlist->IsKadFirewallCheckIP(ip)) )
284					{
285						OnError(ERR_ENCRYPTION_NOTALLOWED);
286						return 0;
287					} else {
288						//AddDebugLogLine(DLP_DEFAULT, false, _T("Incoming unencrypted firewallcheck connection permitted despite RequireEncryption setting  - %s"), DbgGetIPString() );
289					}
290				}
291				return m_nObfusicationBytesReceived; // buffer was unchanged, we can just pass it through
292			}
293		}
294		case ECS_ENCRYPTING:
295			//printf("Encryption enabled on data receiving, decrypting and passing along\n");
296			// basic obfusication enabled and set, so decrypt and pass along
297			m_pfiReceiveBuffer.RC4Crypt((uint8_t*)lpBuf, (uint8_t*)lpBuf, m_nObfusicationBytesReceived);
298			//DumpMem(lpBuf, m_nObfusicationBytesReceived, wxT("Directly decrypted data:"));
299			return m_nObfusicationBytesReceived;
300		case ECS_NEGOTIATING:{
301			//printf("Negotiating on data receive\n");
302			const uint32_t nRead = Negotiate((uint8_t*)lpBuf, m_nObfusicationBytesReceived);
303			if (nRead == (uint32_t)(-1)) {
304				//printf("-> Encryption read error on negotiation\n");
305				return 0;
306			} else if (nRead != (uint32_t)m_nObfusicationBytesReceived && m_StreamCryptState != ECS_ENCRYPTING) {
307				//printf("-> Too much data, bailing out of negotiation step\n");
308				// this means we have more data then the current negotiation step required (or there is a bug) and this should never happen
309				//DebugLogError(_T("CEncryptedStreamSocket: Client %s sent more data then expected while negotiating, disconnecting (2)"), DbgGetIPString());
310				OnError(ERR_ENCRYPTION);
311				return 0;
312			} else if (nRead != (uint32_t)m_nObfusicationBytesReceived && m_StreamCryptState == ECS_ENCRYPTING) {
313				//printf("-> Handshake negotiation finished\n");
314				// we finished the handshake and if we this was an outgoing connection it is allowed (but strange and unlikely) that the client sent payload
315				//DebugLogWarning(_T("CEncryptedStreamSocket: Client %s has finished the handshake but also sent payload on a outgoing connection"), DbgGetIPString());
316				memmove(lpBuf, (uint8_t*)lpBuf + nRead, m_nObfusicationBytesReceived - nRead);
317				return m_nObfusicationBytesReceived - nRead;
318			} else {
319				//printf("-> Negotiation went probably ok\n");
320				return 0;
321			}
322		}
323		default:
324			wxFAIL;
325			return m_nObfusicationBytesReceived;
326	}
327}
328
329void CEncryptedStreamSocket::OnSend(int)
330{
331	// if the socket just connected and this is outgoing, we might want to start the handshake here
332	if (m_StreamCryptState == ECS_PENDING || m_StreamCryptState == ECS_PENDING_SERVER){
333		//printf("Starting connection negotiation on OnSend for %s\n", (const char*) unicode2char(DbgGetIPString()));
334		StartNegotiation(true);
335		return;
336	}
337
338	// check if we have negotiating data pending
339	if (!m_pfiSendBuffer.IsEmpty()) {
340		wxASSERT( m_StreamCryptState >= ECS_NEGOTIATING );
341		SendNegotiatingData(NULL, 0);
342	}
343}
344
345void CEncryptedStreamSocket::CryptPrepareSendData(uint8* pBuffer, uint32 nLen)
346{
347	if (!IsEncryptionLayerReady()) {
348		wxFAIL; // must be a bug
349		return;
350	}
351	if (m_StreamCryptState == ECS_UNKNOWN) {
352		//this happens when the encryption option was not set on an outgoing connection
353		//or if we try to send before receiving on an incoming connection - both shouldn't happen
354		m_StreamCryptState = ECS_NONE;
355		//DebugLogError(_T("CEncryptedStreamSocket: Overwriting State ECS_UNKNOWN with ECS_NONE because of premature Send() (%s)"), DbgGetIPString());
356	}
357	if (m_StreamCryptState == ECS_ENCRYPTING) {
358		//printf("Preparing crypt data on %s\n", (const char*) unicode2char(DbgGetIPString()));
359		//DumpMem(pBuffer, nLen, wxT("Before crypt prepare:\n"));
360		m_pfiSendBuffer.RC4Crypt(pBuffer, pBuffer, nLen);
361		//DumpMem(pBuffer, nLen, wxT("After crypt prepare:\n"));
362	}
363}
364
365/* Internals, just for this class (can be raped) */
366
367bool CEncryptedStreamSocket::IsEncryptionLayerReady()
368{
369	return ( (m_StreamCryptState == ECS_NONE || m_StreamCryptState == ECS_ENCRYPTING || m_StreamCryptState == ECS_UNKNOWN )
370		&& (m_pfiSendBuffer.IsEmpty() || (m_bServerCrypt && m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING)) );
371}
372
373void CEncryptedStreamSocket::StartNegotiation(bool bOutgoing)
374{
375	//printf("Starting socket negotiation\n");
376	if (!bOutgoing) {
377		//printf("Incoming connection negotiation on %s\n", (const char*) unicode2char(DbgGetIPString()));
378		m_NegotiatingState = ONS_BASIC_CLIENTA_RANDOMPART;
379		m_StreamCryptState = ECS_NEGOTIATING;
380		m_nReceiveBytesWanted = 4;
381	} else if (m_StreamCryptState == ECS_PENDING) {
382		//printf("Socket is client.pending on negotiation\n");
383		CMemFile fileRequest(29);
384		const uint8_t bySemiRandomNotProtocolMarker = GetSemiRandomNotProtocolMarker();
385		fileRequest.WriteUInt8(bySemiRandomNotProtocolMarker);
386		fileRequest.WriteUInt32(m_nRandomKeyPart);
387		fileRequest.WriteUInt32(MAGICVALUE_SYNC);
388		const uint8_t bySupportedEncryptionMethod = ENM_OBFUSCATION; // we do not support any further encryption in this version
389		fileRequest.WriteUInt8(bySupportedEncryptionMethod);
390		fileRequest.WriteUInt8(bySupportedEncryptionMethod); // so we also prefer this one
391		uint8_t byPadding = (uint8_t)(GetRandomUint8() % (thePrefs::GetCryptTCPPaddingLength() + 1));
392		fileRequest.WriteUInt8(byPadding);
393		for (int i = 0; i < byPadding; i++) {
394			fileRequest.WriteUInt8(GetRandomUint8());
395		}
396
397		m_NegotiatingState = ONS_BASIC_CLIENTB_MAGICVALUE;
398		m_StreamCryptState = ECS_NEGOTIATING;
399		m_nReceiveBytesWanted = 4;
400
401		SendNegotiatingData(fileRequest.GetRawBuffer(), (uint32_t)fileRequest.GetLength(), 5);
402	} else if (m_StreamCryptState == ECS_PENDING_SERVER) {
403		//printf("Socket is server.pending on negotiation\n");
404		CMemFile fileRequest(113);
405		const uint8_t bySemiRandomNotProtocolMarker = GetSemiRandomNotProtocolMarker();
406		fileRequest.WriteUInt8(bySemiRandomNotProtocolMarker);
407
408		m_cryptDHA.Randomize((CryptoPP::AutoSeededRandomPool&)GetRandomPool(), DHAGREEMENT_A_BITS); // our random a
409		wxASSERT( m_cryptDHA.MinEncodedSize() <= DHAGREEMENT_A_BITS / 8 );
410		CryptoPP::Integer cryptDHPrime((byte*)dh768_p, PRIMESIZE_BYTES);  // our fixed prime
411		// calculate g^a % p
412		CryptoPP::Integer cryptDHGexpAmodP = a_exp_b_mod_c(CryptoPP::Integer(2), m_cryptDHA, cryptDHPrime);
413		wxASSERT( m_cryptDHA.MinEncodedSize() <= PRIMESIZE_BYTES );
414		// put the result into a buffer
415		uint8_t aBuffer[PRIMESIZE_BYTES];
416		cryptDHGexpAmodP.Encode(aBuffer, PRIMESIZE_BYTES);
417
418		fileRequest.Write(aBuffer, PRIMESIZE_BYTES);
419		uint8 byPadding = (uint8)(GetRandomUint8() % 16); // add random padding
420		fileRequest.WriteUInt8(byPadding);
421
422		for (int i = 0; i < byPadding; i++) {
423			fileRequest.WriteUInt8(GetRandomUint8());
424		}
425
426		m_NegotiatingState = ONS_BASIC_SERVER_DHANSWER;
427		m_StreamCryptState = ECS_NEGOTIATING;
428		m_nReceiveBytesWanted = 96;
429
430		SendNegotiatingData(fileRequest.GetRawBuffer(), (uint32_t)fileRequest.GetLength(), (uint32_t)fileRequest.GetLength());
431	} else {
432		wxFAIL;
433		m_StreamCryptState = ECS_NONE;
434		return;
435	}
436}
437
438int CEncryptedStreamSocket::Negotiate(const uint8* pBuffer, uint32 nLen)
439{
440	uint32_t nRead = 0;
441	wxASSERT( m_nReceiveBytesWanted > 0 );
442
443	//DumpMem(pBuffer, nLen, wxT("Negotiate buffer: "));
444
445	try {
446		while (m_NegotiatingState != ONS_COMPLETE && m_nReceiveBytesWanted > 0) {
447			if (m_nReceiveBytesWanted > 512) {
448				wxFAIL;
449				return 0;
450			}
451
452			const uint32_t nToRead =  std::min(nLen - nRead, m_nReceiveBytesWanted);
453			//printf("Reading %i bytes, add from %i position on %i position\n",nToRead, nRead, (int)m_pfiReceiveBuffer.GetPosition());
454			//DumpMem(pBuffer + nRead, nToRead, wxT("Recv Buffer: "));
455			m_pfiReceiveBuffer.Write(pBuffer + nRead, nToRead);
456			nRead += nToRead;
457			m_nReceiveBytesWanted -= nToRead;
458			if (m_nReceiveBytesWanted > 0)  {
459				return nRead;
460			}
461
462			if (m_NegotiatingState != ONS_BASIC_CLIENTA_RANDOMPART && m_NegotiatingState != ONS_BASIC_SERVER_DHANSWER) {
463				// We have the keys, decrypt
464				//printf("We have the keys, so decrypt away on %s\n", (const char*) unicode2char(DbgGetIPString()));
465				m_pfiReceiveBuffer.Encrypt();
466			}
467
468			m_pfiReceiveBuffer.Seek(0);
469
470			switch (m_NegotiatingState) {
471				case ONS_NONE: // would be a bug
472					wxFAIL;
473					return 0;
474				case ONS_BASIC_CLIENTA_RANDOMPART: {
475					//printf("We are on ONS_BASIC_CLIENTA_RANDOMPART, create the keys on %s\n", (const char*) unicode2char(DbgGetIPString()));
476					// This creates the send/receive keys.
477
478					uint8_t achKeyData[21];
479					md4cpy(achKeyData, thePrefs::GetUserHash().GetHash());
480					m_pfiReceiveBuffer.Read(achKeyData + 17, 4);
481
482					achKeyData[16] = MAGICVALUE_REQUESTER;
483
484					//DumpMem(achKeyData, sizeof(achKeyData), wxT("ach:"));
485
486					MD5Sum md5(achKeyData, sizeof(achKeyData));
487					//DumpMem(md5.GetRawHash(), 16, wxT("Md5:"));
488					m_pfiReceiveBuffer.SetKey(md5);
489
490					achKeyData[16] = MAGICVALUE_SERVER;
491					md5.Calculate(achKeyData, sizeof(achKeyData));
492					m_pfiSendBuffer.SetKey(md5);
493
494					m_NegotiatingState = ONS_BASIC_CLIENTA_MAGICVALUE;
495					m_nReceiveBytesWanted = 4;
496					break;
497				}
498				case ONS_BASIC_CLIENTA_MAGICVALUE: {
499					// Check the magic value to confirm encryption works.
500					//printf("Creating magic value on negotiate on %s\n", (const char*) unicode2char(DbgGetIPString()));
501
502					uint32_t dwValue = m_pfiReceiveBuffer.ReadUInt32();
503
504					if (dwValue == MAGICVALUE_SYNC) {
505						// yup, the one or the other way it worked, this is an encrypted stream
506						//DEBUG_ONLY( DebugLog(_T("Received proper magic value, clientIP: %s"), DbgGetIPString()) );
507						// set the receiver key
508						//printf("Magic value works on %s\n", (const char*) unicode2char(DbgGetIPString()));
509						m_NegotiatingState = ONS_BASIC_CLIENTA_METHODTAGSPADLEN;
510						m_nReceiveBytesWanted = 3;
511					} else {
512						//printf("Wrong magic value: 0x%x != 0x%x on %s\n",dwValue, MAGICVALUE_SYNC, (const char*)unicode2char(DbgGetIPString()));
513						//DebugLogError(_T("CEncryptedStreamSocket: Received wrong magic value from clientIP %s on a supposly encrytped stream / Wrong Header"), DbgGetIPString());
514						OnError(ERR_ENCRYPTION);
515						return (-1);
516					}
517					break;
518				}
519				case ONS_BASIC_CLIENTA_METHODTAGSPADLEN: {
520					// Get encryption method and padding.
521					// Might fall back to padding process, but the bytes will be ignored.
522					//printf("Getting encryption method on negotiation\n");
523
524					m_dbgbyEncryptionSupported = m_pfiReceiveBuffer.ReadUInt8();
525					m_dbgbyEncryptionRequested = m_pfiReceiveBuffer.ReadUInt8();
526
527					if (m_dbgbyEncryptionRequested != ENM_OBFUSCATION) {
528						//printf("Unsupported encryption method!\n");
529//						AddDebugLogLine(DLP_LOW, false, _T("CEncryptedStreamSocket: Client %s preffered unsupported encryption method (%i)"), DbgGetIPString(), m_dbgbyEncryptionRequested);
530					}
531
532					m_nReceiveBytesWanted = m_pfiReceiveBuffer.ReadUInt8();
533					m_NegotiatingState = ONS_BASIC_CLIENTA_PADDING;
534
535					if (m_nReceiveBytesWanted > 0) {
536						// No padding
537						break;
538					}
539				}
540				case ONS_BASIC_CLIENTA_PADDING: {
541					//printf("Negotiating on padding, completing\n");
542					// ignore the random bytes, send the response, set status complete
543					CMemFile fileResponse(26);
544					fileResponse.WriteUInt32(MAGICVALUE_SYNC);
545					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
546					fileResponse.WriteUInt8(bySelectedEncryptionMethod);
547
548					amuleIPV4Address address;
549					GetPeer(address);
550					const uint8_t byPaddingLen = theApp->serverconnect->AwaitingTestFromIP(StringIPtoUint32(address.IPAddress())) ? 16 : (thePrefs::GetCryptTCPPaddingLength() + 1);
551					uint8_t byPadding = (uint8_t)(GetRandomUint8() % byPaddingLen);
552
553					fileResponse.WriteUInt8(byPadding);
554					for (int i = 0; i < byPadding; i++) {
555						fileResponse.WriteUInt8((uint8_t)rand());
556					}
557					SendNegotiatingData(fileResponse.GetRawBuffer(), (uint32_t)fileResponse.GetLength());
558					m_NegotiatingState = ONS_COMPLETE;
559					m_StreamCryptState = ECS_ENCRYPTING;
560					//DEBUG_ONLY( DebugLog(_T("CEncryptedStreamSocket: Finished Obufscation handshake with client %s (incoming)"), DbgGetIPString()) );
561					break;
562				}
563				case ONS_BASIC_CLIENTB_MAGICVALUE: {
564					//printf("Negotiating on magic value\n");
565					if (m_pfiReceiveBuffer.ReadUInt32() != MAGICVALUE_SYNC) {
566						//DebugLogError(_T("CEncryptedStreamSocket: EncryptedstreamSyncError: Client sent wrong Magic Value as answer, cannot complete handshake (%s)"), DbgGetIPString());
567						OnError(ERR_ENCRYPTION);
568						return (-1);
569					}
570					m_NegotiatingState = ONS_BASIC_CLIENTB_METHODTAGSPADLEN;
571					m_nReceiveBytesWanted = 2;
572					break;
573				}
574				case ONS_BASIC_CLIENTB_METHODTAGSPADLEN: {
575					//printf("Negotiating on client B pad length\n");
576					m_dbgbyEncryptionMethodSet = m_pfiReceiveBuffer.ReadUInt8();
577					if (m_dbgbyEncryptionMethodSet != ENM_OBFUSCATION) {
578						//DebugLogError( _T("CEncryptedStreamSocket: Client %s set unsupported encryption method (%i), handshake failed"), DbgGetIPString(), m_dbgbyEncryptionMethodSet);
579						OnError(ERR_ENCRYPTION);
580						return (-1);
581					}
582					m_nReceiveBytesWanted = m_pfiReceiveBuffer.ReadUInt8();
583					m_NegotiatingState = ONS_BASIC_CLIENTB_PADDING;
584					if (m_nReceiveBytesWanted > 0) {
585						break;
586					}
587				}
588				case ONS_BASIC_CLIENTB_PADDING:
589					//printf("Negotiating on client B padding, handshake complete\n");
590					// ignore the random bytes, the handshake is complete
591					m_NegotiatingState = ONS_COMPLETE;
592					m_StreamCryptState = ECS_ENCRYPTING;
593					//DEBUG_ONLY( DebugLog(_T("CEncryptedStreamSocket: Finished Obufscation handshake with client %s (outgoing)"), DbgGetIPString()) );
594					break;
595				case ONS_BASIC_SERVER_DHANSWER: {
596					wxASSERT( !m_cryptDHA.IsZero() );
597					uint8_t aBuffer[PRIMESIZE_BYTES + 1];
598					m_pfiReceiveBuffer.Read(aBuffer, PRIMESIZE_BYTES);
599					CryptoPP::Integer cryptDHAnswer((byte*)aBuffer, PRIMESIZE_BYTES);
600					CryptoPP::Integer cryptDHPrime((byte*)dh768_p, PRIMESIZE_BYTES);  // our fixed prime
601					CryptoPP::Integer cryptResult = a_exp_b_mod_c(cryptDHAnswer, m_cryptDHA, cryptDHPrime);
602
603					m_cryptDHA = 0;
604					//DEBUG_ONLY( ZeroMemory(aBuffer, sizeof(aBuffer)) );
605					wxASSERT( cryptResult.MinEncodedSize() <= PRIMESIZE_BYTES );
606
607					// create the keys
608					cryptResult.Encode(aBuffer, PRIMESIZE_BYTES);
609					aBuffer[PRIMESIZE_BYTES] = MAGICVALUE_REQUESTER;
610					MD5Sum md5(aBuffer, sizeof(aBuffer));
611					m_pfiSendBuffer.SetKey(md5);
612					aBuffer[PRIMESIZE_BYTES] = MAGICVALUE_SERVER;
613					md5.Calculate(aBuffer, sizeof(aBuffer));
614					m_pfiReceiveBuffer.SetKey(md5);
615
616					m_NegotiatingState = ONS_BASIC_SERVER_MAGICVALUE;
617					m_nReceiveBytesWanted = 4;
618					break;
619				}
620				case ONS_BASIC_SERVER_MAGICVALUE: {
621					uint32_t dwValue = m_pfiReceiveBuffer.ReadUInt32();
622					if (dwValue == MAGICVALUE_SYNC) {
623						// yup, the one or the other way it worked, this is an encrypted stream
624						//DebugLog(_T("Received proper magic value after DH-Agreement from Serverconnection IP: %s"), DbgGetIPString());
625						// set the receiver key
626						m_NegotiatingState = ONS_BASIC_SERVER_METHODTAGSPADLEN;
627						m_nReceiveBytesWanted = 3;
628					} else {
629						//DebugLogError(_T("CEncryptedStreamSocket: Received wrong magic value after DH-Agreement from Serverconnection"), DbgGetIPString());
630						OnError(ERR_ENCRYPTION);
631						return (-1);
632					}
633					break;
634				}
635				case ONS_BASIC_SERVER_METHODTAGSPADLEN:
636					m_dbgbyEncryptionSupported = m_pfiReceiveBuffer.ReadUInt8();
637					m_dbgbyEncryptionRequested = m_pfiReceiveBuffer.ReadUInt8();
638					if (m_dbgbyEncryptionRequested != ENM_OBFUSCATION) {
639	//					AddDebugLogLine(DLP_LOW, false, _T("CEncryptedStreamSocket: Server %s preffered unsupported encryption method (%i)"), DbgGetIPString(), m_dbgbyEncryptionRequested);
640					}
641					m_nReceiveBytesWanted = m_pfiReceiveBuffer.ReadUInt8();
642					m_NegotiatingState = ONS_BASIC_SERVER_PADDING;
643					if (m_nReceiveBytesWanted > 0) {
644						break;
645					}
646				case ONS_BASIC_SERVER_PADDING: {
647					// ignore the random bytes (they are decrypted already), send the response, set status complete
648					CMemFile fileResponse(26);
649					fileResponse.WriteUInt32(MAGICVALUE_SYNC);
650					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
651					fileResponse.WriteUInt8(bySelectedEncryptionMethod);
652
653					// Server callback connection only allows 16 bytes of padding.
654					uint8_t byPadding = (uint8_t)(GetRandomUint8() % 16);
655					fileResponse.WriteUInt8(byPadding);
656
657					for (int i = 0; i < byPadding; i++) {
658						fileResponse.WriteUInt8((uint8_t)rand());
659					}
660
661					m_NegotiatingState = ONS_BASIC_SERVER_DELAYEDSENDING;
662					SendNegotiatingData(fileResponse.GetRawBuffer(), (uint32_t)fileResponse.GetLength(), 0, true); // don't actually send it right now, store it in our sendbuffer
663					m_StreamCryptState = ECS_ENCRYPTING;
664					//DEBUG_ONLY( DebugLog(_T("CEncryptedStreamSocket: Finished DH Obufscation handshake with Server %s"), DbgGetIPString()) );
665					break;
666				}
667				default:
668					wxFAIL;
669			}
670			m_pfiReceiveBuffer.ResetData();
671		}
672		return nRead;
673	} catch(...) {
674		// can only be caused by a bug in negationhandling, not by the datastream
675		//error->Delete();
676		//printf("Bug on negotiation?\n");
677		wxFAIL;
678		OnError(ERR_ENCRYPTION);
679		m_pfiReceiveBuffer.ResetData();
680		return (-1);
681	}
682}
683
684int CEncryptedStreamSocket::SendNegotiatingData(const void* lpBuf, uint32_t nBufLen, uint32_t nStartCryptFromByte, bool bDelaySend)
685{
686	wxASSERT( m_StreamCryptState == ECS_NEGOTIATING || m_StreamCryptState == ECS_ENCRYPTING );
687	wxASSERT( nStartCryptFromByte <= nBufLen );
688	wxASSERT( m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING || !bDelaySend );
689	//printf("Send negotiation data on %s\n", (const char*) unicode2char(DbgGetIPString()));
690	uint8_t* pBuffer = NULL;
691	bool bProcess = false;
692	if (lpBuf != NULL) {
693		pBuffer = new uint8_t[nBufLen];
694		if (pBuffer == NULL) {
695			throw CMuleException(wxT("Memory exception"), wxT("Memory exception on TCP encrypted socket"));
696		}
697
698		if (nStartCryptFromByte > 0) {
699			memcpy(pBuffer, lpBuf, nStartCryptFromByte);
700		}
701
702		if (nBufLen - nStartCryptFromByte > 0) {
703			//printf("Crypting negotiation data on %s starting on byte %i\n", (const char*) unicode2char(DbgGetIPString()), nStartCryptFromByte);
704			//DumpMem(lpBuf, nBufLen, wxT("Pre-encryption:"));
705			m_pfiSendBuffer.RC4Crypt((uint8*)lpBuf + nStartCryptFromByte, pBuffer + nStartCryptFromByte, nBufLen - nStartCryptFromByte);
706			//DumpMem(pBuffer, nBufLen, wxT("Post-encryption:"));
707		}
708
709		if (!m_pfiSendBuffer.IsEmpty()) {
710			// we already have data pending. Attach it and try to send
711			if (m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING) {
712				m_NegotiatingState = ONS_COMPLETE;
713			} else {
714				wxFAIL;
715			}
716			m_pfiSendBuffer.Append(pBuffer, nBufLen);
717			delete[] pBuffer;
718			pBuffer = NULL;
719			nStartCryptFromByte = 0;
720			bProcess = true; // we want to try to send it right now
721		}
722	}
723
724	if (lpBuf == NULL || bProcess) {
725		// this call is for processing pending data
726		if (m_pfiSendBuffer.IsEmpty() || nStartCryptFromByte != 0) {
727			wxFAIL;
728			return 0;							// or not
729		}
730		nBufLen = (uint32)m_pfiSendBuffer.GetLength();
731		pBuffer = m_pfiSendBuffer.Detach();
732	}
733
734	wxASSERT( m_pfiSendBuffer.IsEmpty() );
735
736	uint32_t result = 0;
737	if (!bDelaySend) {
738		//printf("Writing negotiation data on %s: ", (const char*) unicode2char(DbgGetIPString()));
739		CSocketClientProxy::Write(pBuffer, nBufLen);
740		result = CSocketClientProxy::LastCount();
741		//printf("Wrote %i bytes\n",result);
742	}
743
744	if (result == (uint32_t)SOCKET_ERROR || bDelaySend) {
745		m_pfiSendBuffer.Write(pBuffer, nBufLen);
746		delete[] pBuffer;
747		return result;
748	} else {
749		if (result < nBufLen) {
750			// Store the partial data pending
751			//printf("Partial negotiation pending on %s\n", (const char*) unicode2char(DbgGetIPString()));
752			m_pfiSendBuffer.Write(pBuffer + result, nBufLen - result);
753		}
754		delete[] pBuffer;
755		return result;
756	}
757}
758
759wxString CEncryptedStreamSocket::DbgGetIPString()
760{
761	amuleIPV4Address address;
762	GetPeer(address);
763	return address.IPAddress();
764}
765
766uint8_t CEncryptedStreamSocket::GetSemiRandomNotProtocolMarker() const
767{
768	uint8_t bySemiRandomNotProtocolMarker = 0;
769	bool bOk = false;
770	for (int i = 0; i < 128; i++) {
771		bySemiRandomNotProtocolMarker = GetRandomUint8();
772		switch (bySemiRandomNotProtocolMarker) { // not allowed values
773				case OP_EDONKEYPROT:
774				case OP_PACKEDPROT:
775				case OP_EMULEPROT:
776					break;
777				default:
778					bOk = true;
779		}
780
781		if (bOk) {
782			break;
783		}
784	}
785
786	if (!bOk) {
787		// either we have _real_ bad luck or the randomgenerator is a bit messed up
788		wxFAIL;
789		bySemiRandomNotProtocolMarker = 0x01;
790	}
791	return bySemiRandomNotProtocolMarker;
792}
793