uint8_t CEncryptedStreamSocket::GetSemiRandomNotProtocolMarker() const { uint8_t bySemiRandomNotProtocolMarker = 0; bool bOk = false; for (int i = 0; i < 128; i++) { bySemiRandomNotProtocolMarker = GetRandomUint8(); switch (bySemiRandomNotProtocolMarker) { // not allowed values case OP_EDONKEYPROT: case OP_PACKEDPROT: case OP_EMULEPROT: break; default: bOk = true; } if (bOk) { break; } } if (!bOk) { // either we have _real_ bad luck or the randomgenerator is a bit messed up wxFAIL; bySemiRandomNotProtocolMarker = 0x01; } return bySemiRandomNotProtocolMarker; }
int CEncryptedDatagramSocket::EncryptSendServer(uint8_t** ppbyBuf, int nBufLen, uint32_t dwBaseKey) { wxASSERT( thePrefs::IsServerCryptLayerUDPEnabled() ); wxASSERT( dwBaseKey != 0 ); uint16_t nRandomKeyPart = GetRandomUint16(); uint8_t achKeyData[7]; PokeUInt32(achKeyData, dwBaseKey); achKeyData[4] = MAGICVALUE_UDP_CLIENTSERVER; PokeUInt16(achKeyData + 5, nRandomKeyPart); MD5Sum md5(achKeyData, sizeof(achKeyData)); CRC4EncryptableBuffer sendbuffer; sendbuffer.SetKey(md5, true); // create the semi random byte encryption header uint8_t bySemiRandomNotProtocolMarker = 0; int i; for (i = 0; i < 128; i++) { bySemiRandomNotProtocolMarker = GetRandomUint8(); if (bySemiRandomNotProtocolMarker != OP_EDONKEYPROT) { // not allowed values break; } } if (i >= 128) { // either we have _real_ bad luck or the randomgenerator is a bit messed up wxFAIL; bySemiRandomNotProtocolMarker = 0x01; } uint8_t byPadLen = 0; // padding disabled for UDP currently uint32_t nCryptedLen = nBufLen + byPadLen + CRYPT_HEADER_WITHOUTPADDING; uint8_t* pachCryptedBuffer = new uint8_t[nCryptedLen]; pachCryptedBuffer[0] = bySemiRandomNotProtocolMarker; PokeUInt16(pachCryptedBuffer + 1, nRandomKeyPart); uint32_t dwMagicValue = ENDIAN_SWAP_32(MAGICVALUE_UDP_SYNC_SERVER); sendbuffer.RC4Crypt((uint8_t*)&dwMagicValue, pachCryptedBuffer + 3, 4); sendbuffer.RC4Crypt((uint8_t*)&byPadLen, pachCryptedBuffer + 7, 1); for (int j = 0; j < byPadLen; j++){ uint8_t byRand = (uint8_t)rand(); // they actually don't really need to be random, but it doesn't hurt either sendbuffer.RC4Crypt((uint8_t*)&byRand, pachCryptedBuffer + CRYPT_HEADER_WITHOUTPADDING + j, 1); } sendbuffer.RC4Crypt(*ppbyBuf, pachCryptedBuffer + CRYPT_HEADER_WITHOUTPADDING + byPadLen, nBufLen); delete[] *ppbyBuf; *ppbyBuf = pachCryptedBuffer; theStats::AddUpOverheadCrypt(nCryptedLen - nBufLen); return nCryptedLen; }
// Encrypt packet. Key used: // clientHashOrKadID != NULL -> clientHashOrKadID // clientHashOrKadID == NULL && kad && receiverVerifyKey != 0 -> receiverVerifyKey // else -> ASSERT int CEncryptedDatagramSocket::EncryptSendClient(uint8_t **buf, int bufLen, const uint8_t *clientHashOrKadID, bool kad, uint32_t receiverVerifyKey, uint32_t senderVerifyKey) { wxASSERT(theApp->GetPublicIP() != 0 || kad); wxASSERT(thePrefs::IsClientCryptLayerSupported()); wxASSERT(clientHashOrKadID != NULL || receiverVerifyKey != 0); wxASSERT((receiverVerifyKey == 0 && senderVerifyKey == 0) || kad); uint8_t padLen = 0; // padding disabled for UDP currently const uint32_t cryptHeaderLen = padLen + CRYPT_HEADER_WITHOUTPADDING + (kad ? 8 : 0); uint32_t cryptedLen = bufLen + cryptHeaderLen; uint8_t *cryptedBuffer = new uint8_t[cryptedLen]; bool kadRecvKeyUsed = false; uint16_t randomKeyPart = GetRandomUint16(); CRC4EncryptableBuffer sendbuffer; MD5Sum md5; if (kad) { if ((clientHashOrKadID == NULL || CMD4Hash(clientHashOrKadID).IsEmpty()) && receiverVerifyKey != 0) { kadRecvKeyUsed = true; uint8_t keyData[6]; PokeUInt32(keyData, receiverVerifyKey); PokeUInt16(keyData+4, randomKeyPart); md5.Calculate(keyData, sizeof(keyData)); //DEBUG_ONLY( DebugLog(_T("Creating obfuscated Kad packet encrypted by ReceiverKey (%u)"), nReceiverVerifyKey) ); } else if (clientHashOrKadID != NULL && !CMD4Hash(clientHashOrKadID).IsEmpty()) { uint8_t keyData[18]; md4cpy(keyData, clientHashOrKadID); PokeUInt16(keyData+16, randomKeyPart); md5.Calculate(keyData, sizeof(keyData)); //DEBUG_ONLY( DebugLog(_T("Creating obfuscated Kad packet encrypted by Hash/NodeID %s"), md4str(pachClientHashOrKadID)) ); } else { delete [] cryptedBuffer; wxFAIL; return bufLen; } } else { uint8_t keyData[23]; md4cpy(keyData, clientHashOrKadID); PokeUInt32(keyData+16, theApp->GetPublicIP()); PokeUInt16(keyData+21, randomKeyPart); keyData[20] = MAGICVALUE_UDP; md5.Calculate(keyData, sizeof(keyData)); } sendbuffer.SetKey(md5, true); // create the semi random byte encryption header uint8_t semiRandomNotProtocolMarker = 0; int i; for (i = 0; i < 128; i++) { semiRandomNotProtocolMarker = GetRandomUint8(); semiRandomNotProtocolMarker = kad ? (semiRandomNotProtocolMarker & 0xFE) : (semiRandomNotProtocolMarker | 0x01); // set the ed2k/kad marker bit if (kad) { // set the ed2k/kad and nodeid/recvkey markerbit semiRandomNotProtocolMarker = kadRecvKeyUsed ? ((semiRandomNotProtocolMarker & 0xFE) | 0x02) : (semiRandomNotProtocolMarker & 0xFC); } else { // set the ed2k/kad marker bit semiRandomNotProtocolMarker = (semiRandomNotProtocolMarker | 0x01); } bool bOk = false; switch (semiRandomNotProtocolMarker) { // not allowed values case OP_EMULEPROT: case OP_KADEMLIAPACKEDPROT: case OP_KADEMLIAHEADER: case OP_UDPRESERVEDPROT1: case OP_UDPRESERVEDPROT2: case OP_PACKEDPROT: break; default: bOk = true; } if (bOk) { break; } } if (i >= 128) { // either we have _real_ bad luck or the randomgenerator is a bit messed up wxFAIL; semiRandomNotProtocolMarker = 0x01; } cryptedBuffer[0] = semiRandomNotProtocolMarker; PokeUInt16(cryptedBuffer + 1, randomKeyPart); uint32_t magicValue = ENDIAN_SWAP_32(MAGICVALUE_UDP_SYNC_CLIENT); sendbuffer.RC4Crypt((uint8_t*)&magicValue, cryptedBuffer + 3, 4); sendbuffer.RC4Crypt((uint8_t*)&padLen, cryptedBuffer + 7, 1); for (int j = 0; j < padLen; j++) { uint8_t byRand = (uint8_t)rand(); // they actually don't really need to be random, but it doesn't hurt either sendbuffer.RC4Crypt((uint8_t*)&byRand, cryptedBuffer + CRYPT_HEADER_WITHOUTPADDING + j, 1); } if (kad) { ENDIAN_SWAP_I_32(receiverVerifyKey); ENDIAN_SWAP_I_32(senderVerifyKey); sendbuffer.RC4Crypt((uint8_t*)&receiverVerifyKey, cryptedBuffer + CRYPT_HEADER_WITHOUTPADDING + padLen, 4); sendbuffer.RC4Crypt((uint8_t*)&senderVerifyKey, cryptedBuffer + CRYPT_HEADER_WITHOUTPADDING + padLen + 4, 4); } sendbuffer.RC4Crypt(*buf, cryptedBuffer + cryptHeaderLen, bufLen); delete [] *buf; *buf = cryptedBuffer; theStats::AddUpOverheadCrypt(cryptedLen - bufLen); return cryptedLen; }
int CEncryptedStreamSocket::Negotiate(const uint8* pBuffer, uint32 nLen) { uint32_t nRead = 0; wxASSERT( m_nReceiveBytesWanted > 0 ); //DumpMem(pBuffer, nLen, wxT("Negotiate buffer: ")); try { while (m_NegotiatingState != ONS_COMPLETE && m_nReceiveBytesWanted > 0) { if (m_nReceiveBytesWanted > 512) { wxFAIL; return 0; } const uint32_t nToRead = std::min(nLen - nRead, m_nReceiveBytesWanted); //printf("Reading %i bytes, add from %i position on %i position\n",nToRead, nRead, (int)m_pfiReceiveBuffer.GetPosition()); //DumpMem(pBuffer + nRead, nToRead, wxT("Recv Buffer: ")); m_pfiReceiveBuffer.Write(pBuffer + nRead, nToRead); nRead += nToRead; m_nReceiveBytesWanted -= nToRead; if (m_nReceiveBytesWanted > 0) { return nRead; } if (m_NegotiatingState != ONS_BASIC_CLIENTA_RANDOMPART && m_NegotiatingState != ONS_BASIC_SERVER_DHANSWER) { // We have the keys, decrypt //printf("We have the keys, so decrypt away on %s\n", (const char*) unicode2char(DbgGetIPString())); m_pfiReceiveBuffer.Encrypt(); } m_pfiReceiveBuffer.Seek(0); switch (m_NegotiatingState) { case ONS_NONE: // would be a bug wxFAIL; return 0; case ONS_BASIC_CLIENTA_RANDOMPART: { //printf("We are on ONS_BASIC_CLIENTA_RANDOMPART, create the keys on %s\n", (const char*) unicode2char(DbgGetIPString())); // This creates the send/receive keys. uint8_t achKeyData[21]; md4cpy(achKeyData, thePrefs::GetUserHash().GetHash()); m_pfiReceiveBuffer.Read(achKeyData + 17, 4); achKeyData[16] = MAGICVALUE_REQUESTER; //DumpMem(achKeyData, sizeof(achKeyData), wxT("ach:")); MD5Sum md5(achKeyData, sizeof(achKeyData)); //DumpMem(md5.GetRawHash(), 16, wxT("Md5:")); m_pfiReceiveBuffer.SetKey(md5); achKeyData[16] = MAGICVALUE_SERVER; md5.Calculate(achKeyData, sizeof(achKeyData)); m_pfiSendBuffer.SetKey(md5); m_NegotiatingState = ONS_BASIC_CLIENTA_MAGICVALUE; m_nReceiveBytesWanted = 4; break; } case ONS_BASIC_CLIENTA_MAGICVALUE: { // Check the magic value to confirm encryption works. //printf("Creating magic value on negotiate on %s\n", (const char*) unicode2char(DbgGetIPString())); uint32_t dwValue = m_pfiReceiveBuffer.ReadUInt32(); if (dwValue == MAGICVALUE_SYNC) { // yup, the one or the other way it worked, this is an encrypted stream //DEBUG_ONLY( DebugLog(_T("Received proper magic value, clientIP: %s"), DbgGetIPString()) ); // set the receiver key //printf("Magic value works on %s\n", (const char*) unicode2char(DbgGetIPString())); m_NegotiatingState = ONS_BASIC_CLIENTA_METHODTAGSPADLEN; m_nReceiveBytesWanted = 3; } else { //printf("Wrong magic value: 0x%x != 0x%x on %s\n",dwValue, MAGICVALUE_SYNC, (const char*)unicode2char(DbgGetIPString())); //DebugLogError(_T("CEncryptedStreamSocket: Received wrong magic value from clientIP %s on a supposly encrytped stream / Wrong Header"), DbgGetIPString()); OnError(ERR_ENCRYPTION); return (-1); } break; } case ONS_BASIC_CLIENTA_METHODTAGSPADLEN: { // Get encryption method and padding. // Might fall back to padding process, but the bytes will be ignored. //printf("Getting encryption method on negotiation\n"); m_dbgbyEncryptionSupported = m_pfiReceiveBuffer.ReadUInt8(); m_dbgbyEncryptionRequested = m_pfiReceiveBuffer.ReadUInt8(); if (m_dbgbyEncryptionRequested != ENM_OBFUSCATION) { //printf("Unsupported encryption method!\n"); // AddDebugLogLine(DLP_LOW, false, _T("CEncryptedStreamSocket: Client %s preffered unsupported encryption method (%i)"), DbgGetIPString(), m_dbgbyEncryptionRequested); } m_nReceiveBytesWanted = m_pfiReceiveBuffer.ReadUInt8(); m_NegotiatingState = ONS_BASIC_CLIENTA_PADDING; if (m_nReceiveBytesWanted > 0) { // No padding break; } } case ONS_BASIC_CLIENTA_PADDING: { //printf("Negotiating on padding, completing\n"); // ignore the random bytes, send the response, set status complete CMemFile fileResponse(26); fileResponse.WriteUInt32(MAGICVALUE_SYNC); 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 fileResponse.WriteUInt8(bySelectedEncryptionMethod); amuleIPV4Address address; GetPeer(address); const uint8_t byPaddingLen = theApp->serverconnect->AwaitingTestFromIP(StringIPtoUint32(address.IPAddress())) ? 16 : (thePrefs::GetCryptTCPPaddingLength() + 1); uint8_t byPadding = (uint8_t)(GetRandomUint8() % byPaddingLen); fileResponse.WriteUInt8(byPadding); for (int i = 0; i < byPadding; i++) { fileResponse.WriteUInt8((uint8_t)rand()); } SendNegotiatingData(fileResponse.GetRawBuffer(), (uint32_t)fileResponse.GetLength()); m_NegotiatingState = ONS_COMPLETE; m_StreamCryptState = ECS_ENCRYPTING; //DEBUG_ONLY( DebugLog(_T("CEncryptedStreamSocket: Finished Obufscation handshake with client %s (incoming)"), DbgGetIPString()) ); break; } case ONS_BASIC_CLIENTB_MAGICVALUE: { //printf("Negotiating on magic value\n"); if (m_pfiReceiveBuffer.ReadUInt32() != MAGICVALUE_SYNC) { //DebugLogError(_T("CEncryptedStreamSocket: EncryptedstreamSyncError: Client sent wrong Magic Value as answer, cannot complete handshake (%s)"), DbgGetIPString()); OnError(ERR_ENCRYPTION); return (-1); } m_NegotiatingState = ONS_BASIC_CLIENTB_METHODTAGSPADLEN; m_nReceiveBytesWanted = 2; break; } case ONS_BASIC_CLIENTB_METHODTAGSPADLEN: { //printf("Negotiating on client B pad length\n"); m_dbgbyEncryptionMethodSet = m_pfiReceiveBuffer.ReadUInt8(); if (m_dbgbyEncryptionMethodSet != ENM_OBFUSCATION) { //DebugLogError( _T("CEncryptedStreamSocket: Client %s set unsupported encryption method (%i), handshake failed"), DbgGetIPString(), m_dbgbyEncryptionMethodSet); OnError(ERR_ENCRYPTION); return (-1); } m_nReceiveBytesWanted = m_pfiReceiveBuffer.ReadUInt8(); m_NegotiatingState = ONS_BASIC_CLIENTB_PADDING; if (m_nReceiveBytesWanted > 0) { break; } } case ONS_BASIC_CLIENTB_PADDING: //printf("Negotiating on client B padding, handshake complete\n"); // ignore the random bytes, the handshake is complete m_NegotiatingState = ONS_COMPLETE; m_StreamCryptState = ECS_ENCRYPTING; //DEBUG_ONLY( DebugLog(_T("CEncryptedStreamSocket: Finished Obufscation handshake with client %s (outgoing)"), DbgGetIPString()) ); break; case ONS_BASIC_SERVER_DHANSWER: { wxASSERT( !m_cryptDHA.IsZero() ); uint8_t aBuffer[PRIMESIZE_BYTES + 1]; m_pfiReceiveBuffer.Read(aBuffer, PRIMESIZE_BYTES); CryptoPP::Integer cryptDHAnswer((byte*)aBuffer, PRIMESIZE_BYTES); CryptoPP::Integer cryptDHPrime((byte*)dh768_p, PRIMESIZE_BYTES); // our fixed prime CryptoPP::Integer cryptResult = a_exp_b_mod_c(cryptDHAnswer, m_cryptDHA, cryptDHPrime); m_cryptDHA = 0; //DEBUG_ONLY( ZeroMemory(aBuffer, sizeof(aBuffer)) ); wxASSERT( cryptResult.MinEncodedSize() <= PRIMESIZE_BYTES ); // create the keys cryptResult.Encode(aBuffer, PRIMESIZE_BYTES); aBuffer[PRIMESIZE_BYTES] = MAGICVALUE_REQUESTER; MD5Sum md5(aBuffer, sizeof(aBuffer)); m_pfiSendBuffer.SetKey(md5); aBuffer[PRIMESIZE_BYTES] = MAGICVALUE_SERVER; md5.Calculate(aBuffer, sizeof(aBuffer)); m_pfiReceiveBuffer.SetKey(md5); m_NegotiatingState = ONS_BASIC_SERVER_MAGICVALUE; m_nReceiveBytesWanted = 4; break; } case ONS_BASIC_SERVER_MAGICVALUE: { uint32_t dwValue = m_pfiReceiveBuffer.ReadUInt32(); if (dwValue == MAGICVALUE_SYNC) { // yup, the one or the other way it worked, this is an encrypted stream //DebugLog(_T("Received proper magic value after DH-Agreement from Serverconnection IP: %s"), DbgGetIPString()); // set the receiver key m_NegotiatingState = ONS_BASIC_SERVER_METHODTAGSPADLEN; m_nReceiveBytesWanted = 3; } else { //DebugLogError(_T("CEncryptedStreamSocket: Received wrong magic value after DH-Agreement from Serverconnection"), DbgGetIPString()); OnError(ERR_ENCRYPTION); return (-1); } break; } case ONS_BASIC_SERVER_METHODTAGSPADLEN: m_dbgbyEncryptionSupported = m_pfiReceiveBuffer.ReadUInt8(); m_dbgbyEncryptionRequested = m_pfiReceiveBuffer.ReadUInt8(); if (m_dbgbyEncryptionRequested != ENM_OBFUSCATION) { // AddDebugLogLine(DLP_LOW, false, _T("CEncryptedStreamSocket: Server %s preffered unsupported encryption method (%i)"), DbgGetIPString(), m_dbgbyEncryptionRequested); } m_nReceiveBytesWanted = m_pfiReceiveBuffer.ReadUInt8(); m_NegotiatingState = ONS_BASIC_SERVER_PADDING; if (m_nReceiveBytesWanted > 0) { break; } case ONS_BASIC_SERVER_PADDING: { // ignore the random bytes (they are decrypted already), send the response, set status complete CMemFile fileResponse(26); fileResponse.WriteUInt32(MAGICVALUE_SYNC); 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 fileResponse.WriteUInt8(bySelectedEncryptionMethod); // Server callback connection only allows 16 bytes of padding. uint8_t byPadding = (uint8_t)(GetRandomUint8() % 16); fileResponse.WriteUInt8(byPadding); for (int i = 0; i < byPadding; i++) { fileResponse.WriteUInt8((uint8_t)rand()); } m_NegotiatingState = ONS_BASIC_SERVER_DELAYEDSENDING; SendNegotiatingData(fileResponse.GetRawBuffer(), (uint32_t)fileResponse.GetLength(), 0, true); // don't actually send it right now, store it in our sendbuffer m_StreamCryptState = ECS_ENCRYPTING; //DEBUG_ONLY( DebugLog(_T("CEncryptedStreamSocket: Finished DH Obufscation handshake with Server %s"), DbgGetIPString()) ); break; } default: wxFAIL; } m_pfiReceiveBuffer.ResetData(); } return nRead; } catch(...) { // can only be caused by a bug in negationhandling, not by the datastream //error->Delete(); //printf("Bug on negotiation?\n"); wxFAIL; OnError(ERR_ENCRYPTION); m_pfiReceiveBuffer.ResetData(); return (-1); } }
void CEncryptedStreamSocket::StartNegotiation(bool bOutgoing) { //printf("Starting socket negotiation\n"); if (!bOutgoing) { //printf("Incoming connection negotiation on %s\n", (const char*) unicode2char(DbgGetIPString())); m_NegotiatingState = ONS_BASIC_CLIENTA_RANDOMPART; m_StreamCryptState = ECS_NEGOTIATING; m_nReceiveBytesWanted = 4; } else if (m_StreamCryptState == ECS_PENDING) { //printf("Socket is client.pending on negotiation\n"); CMemFile fileRequest(29); const uint8_t bySemiRandomNotProtocolMarker = GetSemiRandomNotProtocolMarker(); fileRequest.WriteUInt8(bySemiRandomNotProtocolMarker); fileRequest.WriteUInt32(m_nRandomKeyPart); fileRequest.WriteUInt32(MAGICVALUE_SYNC); const uint8_t bySupportedEncryptionMethod = ENM_OBFUSCATION; // we do not support any further encryption in this version fileRequest.WriteUInt8(bySupportedEncryptionMethod); fileRequest.WriteUInt8(bySupportedEncryptionMethod); // so we also prefer this one uint8_t byPadding = (uint8_t)(GetRandomUint8() % (thePrefs::GetCryptTCPPaddingLength() + 1)); fileRequest.WriteUInt8(byPadding); for (int i = 0; i < byPadding; i++) { fileRequest.WriteUInt8(GetRandomUint8()); } m_NegotiatingState = ONS_BASIC_CLIENTB_MAGICVALUE; m_StreamCryptState = ECS_NEGOTIATING; m_nReceiveBytesWanted = 4; SendNegotiatingData(fileRequest.GetRawBuffer(), (uint32_t)fileRequest.GetLength(), 5); } else if (m_StreamCryptState == ECS_PENDING_SERVER) { //printf("Socket is server.pending on negotiation\n"); CMemFile fileRequest(113); const uint8_t bySemiRandomNotProtocolMarker = GetSemiRandomNotProtocolMarker(); fileRequest.WriteUInt8(bySemiRandomNotProtocolMarker); m_cryptDHA.Randomize((CryptoPP::AutoSeededRandomPool&)GetRandomPool(), DHAGREEMENT_A_BITS); // our random a wxASSERT( m_cryptDHA.MinEncodedSize() <= DHAGREEMENT_A_BITS / 8 ); CryptoPP::Integer cryptDHPrime((byte*)dh768_p, PRIMESIZE_BYTES); // our fixed prime // calculate g^a % p CryptoPP::Integer cryptDHGexpAmodP = a_exp_b_mod_c(CryptoPP::Integer(2), m_cryptDHA, cryptDHPrime); wxASSERT( m_cryptDHA.MinEncodedSize() <= PRIMESIZE_BYTES ); // put the result into a buffer uint8_t aBuffer[PRIMESIZE_BYTES]; cryptDHGexpAmodP.Encode(aBuffer, PRIMESIZE_BYTES); fileRequest.Write(aBuffer, PRIMESIZE_BYTES); uint8 byPadding = (uint8)(GetRandomUint8() % 16); // add random padding fileRequest.WriteUInt8(byPadding); for (int i = 0; i < byPadding; i++) { fileRequest.WriteUInt8(GetRandomUint8()); } m_NegotiatingState = ONS_BASIC_SERVER_DHANSWER; m_StreamCryptState = ECS_NEGOTIATING; m_nReceiveBytesWanted = 96; SendNegotiatingData(fileRequest.GetRawBuffer(), (uint32_t)fileRequest.GetLength(), (uint32_t)fileRequest.GetLength()); } else { wxFAIL; m_StreamCryptState = ECS_NONE; return; } }