| File: | rootdir/src/EncryptedStreamSocket.cpp |
| Warning: | line 696, column 4 Memory copy function overflows the destination buffer |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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 authentication | |||
| 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_REQUESTER34 34 // modification of the requester-send and server-receive key | |||
| 99 | #define MAGICVALUE_SERVER203 203 // modification of the server-send and requester-send key | |||
| 100 | #define MAGICVALUE_SYNC0x835E6FC4 0x835E6FC4 // value to check if we have a working encrypted stream | |||
| 101 | #define DHAGREEMENT_A_BITS128 128 | |||
| 102 | ||||
| 103 | #define PRIMESIZE_BYTES96 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(-1) | |||
| 117 | #undef SOCKET_ERROR(-1) | |||
| 118 | #endif | |||
| 119 | #define SOCKET_ERROR(-1) (-1) | |||
| 120 | ||||
| 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 | ||||
| 136 | CEncryptedStreamSocket::~CEncryptedStreamSocket() | |||
| 137 | {} | |||
| 138 | ||||
| 139 | ||||
| 140 | /* External interface */ | |||
| 141 | ||||
| 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 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 146, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 147 | } | |||
| 148 | return; | |||
| 149 | } | |||
| 150 | ||||
| 151 | if (bEnabled && pTargetClientHash != NULL__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_REQUESTER34; | |||
| 163 | MD5Sum md5(achKeyData, sizeof(achKeyData)); | |||
| 164 | m_pfiSendBuffer.SetKey(md5); | |||
| 165 | ||||
| 166 | achKeyData[16] = MAGICVALUE_SERVER203; | |||
| 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 )do { if ( !bEnabled ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp", 175, __FUNCTION__, "!bEnabled" , (const char*)__null), wxTrapInAssert) ) { wxTrapInAssert = false ; asm volatile ("int $3"); } } while ( (void)0, 0 ); | |||
| 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 | |||
| 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 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 188, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 189 | return 0; | |||
| 190 | } else if (m_bServerCrypt && m_StreamCryptState == ECS_ENCRYPTING && !m_pfiSendBuffer.IsEmpty()) { | |||
| 191 | wxASSERT( m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING )do { if ( m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 191, __FUNCTION__, "m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING" , (const char*)__null), wxTrapInAssert) ) { wxTrapInAssert = false ; asm volatile ("int $3"); } } while ( (void)0, 0 ); | |||
| 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 )do { if ( nRes != (-1) ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp", 195, __FUNCTION__, "nRes != (-1)" , (const char*)__null), wxTrapInAssert) ) { wxTrapInAssert = false ; asm volatile ("int $3"); } } while ( (void)0, 0 ); | |||
| 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 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 199, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 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 | ||||
| 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(-1) || 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 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 230, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 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_ENCRYPTION0x03); | |||
| 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_NOTALLOWED0x04); | |||
| 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_ENCRYPTION0x03); | |||
| 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 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 320, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 321 | return m_nObfusicationBytesReceived; | |||
| 322 | } | |||
| 323 | } | |||
| 324 | ||||
| 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 )do { if ( m_StreamCryptState >= ECS_NEGOTIATING ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 336, __FUNCTION__, "m_StreamCryptState >= ECS_NEGOTIATING" , (const char*)__null), wxTrapInAssert) ) { wxTrapInAssert = false ; asm volatile ("int $3"); } } while ( (void)0, 0 ); | |||
| 337 | SendNegotiatingData(NULL__null, 0); | |||
| 338 | } | |||
| 339 | } | |||
| 340 | ||||
| 341 | void CEncryptedStreamSocket::CryptPrepareSendData(uint8* pBuffer, uint32 nLen) | |||
| 342 | { | |||
| 343 | if (!IsEncryptionLayerReady()) { | |||
| 344 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 344, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); // 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 | ||||
| 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 | ||||
| 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
| |||
| 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_SYNC0x835E6FC4); | |||
| 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_BITS128); // our random a | |||
| 405 | wxASSERT( m_cryptDHA.MinEncodedSize() <= DHAGREEMENT_A_BITS / 8 )do { if ( m_cryptDHA.MinEncodedSize() <= 128 / 8 ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 405, __FUNCTION__, "m_cryptDHA.MinEncodedSize() <= 128 / 8" , (const char*)__null), wxTrapInAssert) ) { wxTrapInAssert = false ; asm volatile ("int $3"); } } while ( (void)0, 0 ); | |||
| 406 | CryptoPP::Integer cryptDHPrime((uint8_t*)dh768_p, PRIMESIZE_BYTES96); // 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 )do { if ( m_cryptDHA.MinEncodedSize() <= 96 ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 409, __FUNCTION__, "m_cryptDHA.MinEncodedSize() <= 96", ( const char*)__null), wxTrapInAssert) ) { wxTrapInAssert = false ; asm volatile ("int $3"); } } while ( (void)0, 0 ); | |||
| 410 | // put the result into a buffer | |||
| 411 | uint8_t aBuffer[PRIMESIZE_BYTES96]; | |||
| 412 | cryptDHGexpAmodP.Encode(aBuffer, PRIMESIZE_BYTES96); | |||
| 413 | ||||
| 414 | fileRequest.Write(aBuffer, PRIMESIZE_BYTES96); | |||
| 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 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 428, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 429 | m_StreamCryptState = ECS_NONE; | |||
| 430 | return; | |||
| 431 | } | |||
| 432 | } | |||
| 433 | ||||
| 434 | int CEncryptedStreamSocket::Negotiate(const uint8* pBuffer, uint32 nLen) | |||
| 435 | { | |||
| 436 | uint32_t nRead = 0; | |||
| 437 | wxASSERT( m_nReceiveBytesWanted > 0 )do { if ( m_nReceiveBytesWanted > 0 ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp", 437, __FUNCTION__ , "m_nReceiveBytesWanted > 0", (const char*)__null), wxTrapInAssert ) ) { wxTrapInAssert = false; asm volatile ("int $3"); } } while ( (void)0, 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 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 444, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 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 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 468, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 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_REQUESTER34; | |||
| 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_SERVER203; | |||
| 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_SYNC0x835E6FC4) { | |||
| 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 encrypted stream / Wrong Header"), GetPeer()); | |||
| 510 | OnError(ERR_ENCRYPTION0x03); | |||
| 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 preferred 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_SYNC0x835E6FC4); | |||
| 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_SYNC0x835E6FC4) { | |||
| 561 | //DebugLogError(_T("CEncryptedStreamSocket: EncryptedstreamSyncError: Client sent wrong Magic Value as answer, cannot complete handshake (%s)"), GetPeer()); | |||
| 562 | OnError(ERR_ENCRYPTION0x03); | |||
| 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_ENCRYPTION0x03); | |||
| 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() )do { if ( !m_cryptDHA.IsZero() ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp", 592, __FUNCTION__ , "!m_cryptDHA.IsZero()", (const char*)__null), wxTrapInAssert ) ) { wxTrapInAssert = false; asm volatile ("int $3"); } } while ( (void)0, 0 ); | |||
| 593 | uint8_t aBuffer[PRIMESIZE_BYTES96 + 1]; | |||
| 594 | m_pfiReceiveBuffer.Read(aBuffer, PRIMESIZE_BYTES96); | |||
| 595 | CryptoPP::Integer cryptDHAnswer((uint8_t*)aBuffer, PRIMESIZE_BYTES96); | |||
| 596 | CryptoPP::Integer cryptDHPrime((uint8_t*)dh768_p, PRIMESIZE_BYTES96); // 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 )do { if ( cryptResult.MinEncodedSize() <= 96 ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 601, __FUNCTION__, "cryptResult.MinEncodedSize() <= 96", (const char*)__null), wxTrapInAssert) ) { wxTrapInAssert = false ; asm volatile ("int $3"); } } while ( (void)0, 0 ); | |||
| 602 | ||||
| 603 | // create the keys | |||
| 604 | cryptResult.Encode(aBuffer, PRIMESIZE_BYTES96); | |||
| 605 | aBuffer[PRIMESIZE_BYTES96] = MAGICVALUE_REQUESTER34; | |||
| 606 | MD5Sum md5(aBuffer, sizeof(aBuffer)); | |||
| 607 | m_pfiSendBuffer.SetKey(md5); | |||
| 608 | aBuffer[PRIMESIZE_BYTES96] = MAGICVALUE_SERVER203; | |||
| 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_SYNC0x835E6FC4) { | |||
| 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_ENCRYPTION0x03); | |||
| 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 preferred 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_SYNC0x835E6FC4); | |||
| 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 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 665, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 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 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 674, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 675 | OnError(ERR_ENCRYPTION0x03); | |||
| 676 | m_pfiReceiveBuffer.ResetData(); | |||
| 677 | return (-1); | |||
| 678 | } | |||
| 679 | } | |||
| 680 | ||||
| 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 )do { if ( m_StreamCryptState == ECS_NEGOTIATING || m_StreamCryptState == ECS_ENCRYPTING ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp", 683, __FUNCTION__, "m_StreamCryptState == ECS_NEGOTIATING || m_StreamCryptState == ECS_ENCRYPTING" , (const char*)__null), wxTrapInAssert) ) { wxTrapInAssert = false ; asm volatile ("int $3"); } } while ( (void)0, 0 ); | |||
| 684 | wxASSERT( nStartCryptFromByte <= nBufLen )do { if ( nStartCryptFromByte <= nBufLen ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp", 684, __FUNCTION__ , "nStartCryptFromByte <= nBufLen", (const char*)__null), wxTrapInAssert ) ) { wxTrapInAssert = false; asm volatile ("int $3"); } } while ( (void)0, 0 ); | |||
| 685 | wxASSERT( m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING || !bDelaySend )do { if ( m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING || !bDelaySend ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp", 685, __FUNCTION__, "m_NegotiatingState == ONS_BASIC_SERVER_DELAYEDSENDING || !bDelaySend" , (const char*)__null), wxTrapInAssert) ) { wxTrapInAssert = false ; asm volatile ("int $3"); } } while ( (void)0, 0 ); | |||
| 686 | //printf("Send negotiation data on %s\n", (const char*) unicode2char(GetPeer())); | |||
| 687 | uint8_t* pBuffer = NULL__null; | |||
| 688 | bool bProcess = false; | |||
| 689 | if (lpBuf != NULL__null) { | |||
| 690 | pBuffer = new uint8_t[nBufLen]; | |||
| 691 | if (pBuffer
| |||
| 692 | throw CMuleException(wxT("Memory exception")L"Memory exception", wxT("Memory exception on TCP encrypted socket")L"Memory exception on TCP encrypted socket"); | |||
| 693 | } | |||
| 694 | ||||
| 695 | if (nStartCryptFromByte
| |||
| 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 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 711, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 712 | } | |||
| 713 | m_pfiSendBuffer.Append(pBuffer, nBufLen); | |||
| 714 | delete[] pBuffer; | |||
| 715 | pBuffer = NULL__null; | |||
| 716 | nStartCryptFromByte = 0; | |||
| 717 | bProcess = true; // we want to try to send it right now | |||
| 718 | } | |||
| 719 | } | |||
| 720 | ||||
| 721 | if (lpBuf == NULL__null || bProcess) { | |||
| 722 | // this call is for processing pending data | |||
| 723 | if (m_pfiSendBuffer.IsEmpty() || nStartCryptFromByte != 0) { | |||
| 724 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 724, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 725 | return 0; // or not | |||
| 726 | } | |||
| 727 | nBufLen = (uint32)m_pfiSendBuffer.GetLength(); | |||
| 728 | pBuffer = m_pfiSendBuffer.Detach(); | |||
| 729 | } | |||
| 730 | ||||
| 731 | wxASSERT( m_pfiSendBuffer.IsEmpty() )do { if ( m_pfiSendBuffer.IsEmpty() ) { } else if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp", 731, __FUNCTION__ , "m_pfiSendBuffer.IsEmpty()", (const char*)__null), wxTrapInAssert ) ) { wxTrapInAssert = false; asm volatile ("int $3"); } } while ( (void)0, 0 ); | |||
| 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(-1) || 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 | ||||
| 756 | 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 | wxFAILdo { if ( wxTheAssertHandler && (wxOnAssert("EncryptedStreamSocket.cpp" , 778, __FUNCTION__, "\"Assert failure\"", (const char*)__null ), wxTrapInAssert) ) { wxTrapInAssert = false; asm volatile ( "int $3"); } } while ( (void)0, 0 ); | |||
| 779 | bySemiRandomNotProtocolMarker = 0x01; | |||
| 780 | } | |||
| 781 | return bySemiRandomNotProtocolMarker; | |||
| 782 | } |