diff options
Diffstat (limited to 'libktorrent/mse')
-rw-r--r-- | libktorrent/mse/Makefile.am | 9 | ||||
-rw-r--r-- | libktorrent/mse/bigint.cpp | 100 | ||||
-rw-r--r-- | libktorrent/mse/bigint.h | 98 | ||||
-rw-r--r-- | libktorrent/mse/encryptedauthenticate.cpp | 302 | ||||
-rw-r--r-- | libktorrent/mse/encryptedauthenticate.h | 82 | ||||
-rw-r--r-- | libktorrent/mse/encryptedserverauthenticate.cpp | 354 | ||||
-rw-r--r-- | libktorrent/mse/encryptedserverauthenticate.h | 80 | ||||
-rw-r--r-- | libktorrent/mse/functions.cpp | 74 | ||||
-rw-r--r-- | libktorrent/mse/functions.h | 39 | ||||
-rw-r--r-- | libktorrent/mse/rc4encryptor.cpp | 100 | ||||
-rw-r--r-- | libktorrent/mse/rc4encryptor.h | 96 | ||||
-rw-r--r-- | libktorrent/mse/streamsocket.cpp | 326 | ||||
-rw-r--r-- | libktorrent/mse/streamsocket.h | 185 |
13 files changed, 1845 insertions, 0 deletions
diff --git a/libktorrent/mse/Makefile.am b/libktorrent/mse/Makefile.am new file mode 100644 index 0000000..d6a8ac5 --- /dev/null +++ b/libktorrent/mse/Makefile.am @@ -0,0 +1,9 @@ +INCLUDES = -I$(srcdir)/.. $(all_includes) +METASOURCES = AUTO +libmse_la_LDFLAGS = -lgmp $(all_libraries) +noinst_LTLIBRARIES = libmse.la +noinst_HEADERS = bigint.h rc4encryptor.h streamsocket.h encryptedauthenticate.h \ + encryptedserverauthenticate.h functions.h +libmse_la_SOURCES = bigint.cpp rc4encryptor.cpp streamsocket.cpp \ + encryptedauthenticate.cpp encryptedserverauthenticate.cpp functions.cpp +KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI) diff --git a/libktorrent/mse/bigint.cpp b/libktorrent/mse/bigint.cpp new file mode 100644 index 0000000..90c6d9e --- /dev/null +++ b/libktorrent/mse/bigint.cpp @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2005 by Joris Guisson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include <time.h> +#include <string.h> +#include <stdlib.h> +#include <util/log.h> +#include <util/functions.h> +#include <torrent/globals.h> +#include "bigint.h" + +using namespace bt; + +namespace mse +{ + + + BigInt::BigInt(Uint32 num_bits) + { + mpz_init2(val,num_bits); + } + + BigInt::BigInt(const QString & value) + { + mpz_init2(val,(value.length() - 2)*4); + mpz_set_str(val,value.ascii(),0); + } + + BigInt::BigInt(const BigInt & bi) + { + mpz_set(val,bi.val); + } + + BigInt::~BigInt() + { + mpz_clear(val); + } + + + BigInt & BigInt::operator = (const BigInt & bi) + { + mpz_set(val,bi.val); + return *this; + } + + BigInt BigInt::powerMod(const BigInt & x,const BigInt & e,const BigInt & d) + { + BigInt r; + mpz_powm(r.val,x.val,e.val,d.val); + return r; + } + + BigInt BigInt::random() + { + static Uint32 rnd = 0; + if (rnd % 10 == 0) + { + TimeStamp now = bt::GetCurrentTime(); + srand(now); + rnd = 0; + } + rnd++; + Uint8 tmp[20]; + for (Uint32 i = 0;i < 20;i++) + tmp[i] = (Uint8)rand() % 0x100; + + return BigInt::fromBuffer(tmp,20); + } + + Uint32 BigInt::toBuffer(Uint8* buf,Uint32 max_size) const + { + size_t foo; + mpz_export(buf,&foo,1,1,1,0,val); + return foo; + } + + BigInt BigInt::fromBuffer(const Uint8* buf,Uint32 size) + { + BigInt r(size*8); + mpz_import(r.val,size,1,1,1,0,buf); + return r; + } + +} diff --git a/libktorrent/mse/bigint.h b/libktorrent/mse/bigint.h new file mode 100644 index 0000000..ad94d20 --- /dev/null +++ b/libktorrent/mse/bigint.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (C) 2005 by Joris Guisson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef MSEBIGINT_H +#define MSEBIGINT_H + +#include <qstring.h> +#include <util/constants.h> +#include <stdio.h> +#include <gmp.h> + +using bt::Uint8; +using bt::Uint16; +using bt::Uint32; +using bt::Uint64; + +namespace mse +{ + + /** + * @author Joris Guisson <[email protected]> + * + * Class which can hold an arbitrary large integer. This will be a very important part of our + * MSE implementation. + */ + class BigInt + { + public: + /** + * Create a big integer, with num_bits bits. + * All bits will be set to 0. + * @param num_bits The number of bits + */ + BigInt(Uint32 num_bits = 0); + + /** + * Create a big integer of a string. The string must be + * a hexadecimal representation of an integer. For example : + * 12AFFE123488BBBE123 + * + * Letters can be upper or lower case. Invalid chars will create an invalid number. + * @param value The hexadecimal representation of the number + */ + BigInt(const QString & value); + + /** + * Copy constructor. + * @param bi BigInt to copy + */ + BigInt(const BigInt & bi); + virtual ~BigInt(); + + /** + * Assignment operator. + * @param bi The BigInt to copy + * @return *this + */ + BigInt & operator = (const BigInt & bi); + + /** + * Calculates + * (x ^ e) mod d + * ^ is power + */ + static BigInt powerMod(const BigInt & x,const BigInt & e,const BigInt & d); + + /// Make a random BigInt + static BigInt random(); + + /// Export the bigint ot a buffer + Uint32 toBuffer(Uint8* buf,Uint32 max_size) const; + + /// Make a BigInt out of a buffer + static BigInt fromBuffer(const Uint8* buf,Uint32 size); + + private: + mpz_t val; + }; + +} + +#endif diff --git a/libktorrent/mse/encryptedauthenticate.cpp b/libktorrent/mse/encryptedauthenticate.cpp new file mode 100644 index 0000000..644ba7b --- /dev/null +++ b/libktorrent/mse/encryptedauthenticate.cpp @@ -0,0 +1,302 @@ +/*************************************************************************** + * Copyright (C) 2005 by Joris Guisson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include <stdlib.h> +#include <algorithm> +#include <util/functions.h> +#include <util/log.h> +#include <torrent/globals.h> +#include <torrent/server.h> +#include "encryptedauthenticate.h" +#include "rc4encryptor.h" +#include "streamsocket.h" +#include "functions.h" + +using namespace bt; + +namespace mse +{ + + + + EncryptedAuthenticate::EncryptedAuthenticate( + const QString& ip, + Uint16 port, + const SHA1Hash& info_hash, + const PeerID& peer_id, + PeerManager* pman) + : Authenticate(ip, port, info_hash, peer_id, pman) + { + mse::GeneratePublicPrivateKey(xa,ya); + state = NOT_CONNECTED; + buf_size = 0; + our_rc4 = 0; + vc_off = 0; + dec_bytes = 0; + crypto_select = 0; + pad_D_len = 0; + end_of_crypto_handshake = 0; + //Out(SYS_CON|LOG_DEBUG) << "EncryptedAuthenticate : " << ip << ":" << port << endl; + } + + + EncryptedAuthenticate::~EncryptedAuthenticate() + { + delete our_rc4; + } + + + + void EncryptedAuthenticate::connected() + { + // we are connected so send ya and some padding + Uint8 tmp[608]; + ya.toBuffer(tmp,96); + sock->sendData(tmp,96 + rand() % 512); + state = SENT_YA; + } + + /* + 1 A->B: Diffie Hellman Ya, PadA + 2 B->A: Diffie Hellman Yb, PadB + 3 A->B: HASH('req1', S), HASH('req2', SKEY) xor HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)), ENCRYPT(IA) + 4 B->A: ENCRYPT(VC, crypto_select, len(padD), padD), ENCRYPT2(Payload Stream) + 5 A->B: ENCRYPT2(Payload Stream) + */ + + + + void EncryptedAuthenticate::handleYB() + { + // if you can't sent 96 bytes you are not worth the effort + if (buf_size < 96) + { + Out(SYS_CON|LOG_DEBUG) << "Not enough data received, encrypted authentication failed" << endl; + onFinish(false); + return; + } + + // read Yb + yb = BigInt::fromBuffer(buf,96); + + // calculate s + s = mse::DHSecret(xa,yb); + + state = GOT_YB; + // now we must send line 3 + Uint8 tmp_buf[120]; // temporary buffer + bt::SHA1Hash h1,h2; // temporary hash + + // generate and send the first hash + memcpy(tmp_buf,"req1",4); + s.toBuffer(tmp_buf + 4,96); + h1 = SHA1Hash::generate(tmp_buf,100); + sock->sendData(h1.getData(),20); + + // generate second and third hash and xor them + memcpy(tmp_buf,"req2",4); + memcpy(tmp_buf+4,info_hash.getData(),20); + h1 = SHA1Hash::generate(tmp_buf,24); + + memcpy(tmp_buf,"req3",4); + s.toBuffer(tmp_buf + 4,96); + h2 = SHA1Hash::generate(tmp_buf,100); + sock->sendData((h1 ^ h2).getData(),20); + + // now we enter encrypted mode the keys are : + // HASH('keyA', S, SKEY) for the encryption key + // HASH('keyB', S, SKEY) for the decryption key + enc = mse::EncryptionKey(true,s,info_hash); + dec = mse::EncryptionKey(false,s,info_hash); + + our_rc4 = new RC4Encryptor(dec,enc); + + // now we must send ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)) + memset(tmp_buf,0,16); // VC are 8 0x00's + if (Globals::instance().getServer().unencryptedConnectionsAllowed()) + tmp_buf[11] = 0x03; // we support both plain text and rc4 + else + tmp_buf[11] = 0x02; + WriteUint16(tmp_buf,12,0x0000); // no padC + WriteUint16(tmp_buf,14,68); // length of IA, which will be the bittorrent handshake + // send IA which is the handshake + makeHandshake(tmp_buf+16,info_hash,our_peer_id); + sock->sendData(our_rc4->encrypt(tmp_buf,84),84); + + // search for the encrypted VC in the data + findVC(); + } + + void EncryptedAuthenticate::findVC() + { + Uint8 vc[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + RC4Encryptor rc4(enc,dec); + memcpy(vc,rc4.encrypt(vc,8),8); + + Uint32 max_i = buf_size - 8; + for (Uint32 i = 96;i < max_i;i++) + { + if (vc[0] == buf[i] && memcmp(buf+i,vc,8) == 0) + { + state = FOUND_VC; + vc_off = i; + handleCryptoSelect(); + return; + } + } + + // we haven't found it in the first 616 bytes (96 + max 512 padding + 8 bytes VC) + if (buf_size >= 616) + { + onFinish(false); + } + } + + void EncryptedAuthenticate::handleCryptoSelect() + { + // not enough data available so lets come back later + if (vc_off + 14 >= buf_size) + return; + + // now decrypt the first 14 bytes + our_rc4->decrypt(buf + vc_off,14); + // check the VC + for (Uint32 i = vc_off;i < vc_off + 8;i++) + { + if (buf[i]) + { + Out(SYS_CON|LOG_DEBUG) << "Invalid VC " << endl; + onFinish(false); + return; + } + } + + crypto_select = ReadUint32(buf,vc_off + 8); + pad_D_len = ReadUint16(buf,vc_off + 12); + if (pad_D_len > 512) + { + Out(SYS_CON|LOG_DEBUG) << "Invalid pad D length" << endl; + onFinish(false); + return; + } + + end_of_crypto_handshake = vc_off + 14 + pad_D_len; + if (!(vc_off + 14 + pad_D_len < buf_size)) + { + // padD is not complete, wait for that + state = WAIT_FOR_PAD_D; + return; + } + + handlePadD(); + } + + void EncryptedAuthenticate::handlePadD() + { + // decrypt the padding + our_rc4->decrypt(buf + (vc_off + 14),pad_D_len); + + bool rc4 = false; + if (crypto_select & 0x00000001) // plain_text selected + { + delete our_rc4; + our_rc4 = 0; + } + else if (crypto_select & 0x00000002) // now it must be rc4 if not exit + { + sock->setRC4Encryptor(our_rc4); + our_rc4 = 0; + rc4 = true; + } + else // we don't support anything else so error out + { + onFinish(false); + return; + } + + // noz we wait for the normal handshake + state = NORMAL_HANDSHAKE; + // if we have read more then the crypto handshake, reinsert it + if (buf_size > vc_off + 14 + pad_D_len) + { + Uint32 off = vc_off + 14 + pad_D_len; + sock->reinsert(buf + off,buf_size - off); + Authenticate::onReadyRead(); + } + } + + void EncryptedAuthenticate::onReadyRead() + { + if (finished) + return; + + + Uint32 ba = sock->bytesAvailable(); + if (ba == 0) + { + onFinish(false); + return; + } + + if (state != NORMAL_HANDSHAKE) + { + if (buf_size + ba > MAX_EA_BUF_SIZE) + ba = MAX_EA_BUF_SIZE - buf_size; + + // do not read past the end of padD + if (pad_D_len > 0 && buf_size + ba > vc_off + 14 + pad_D_len) + ba = (vc_off + 14 + pad_D_len) - buf_size; + // read data + buf_size += sock->readData(buf + buf_size,ba); + + } + + switch (state) + { + case SENT_YA: + if (ba > 608) + { + onFinish(false); + } + else + { + handleYB(); + } + break; + case GOT_YB: + findVC(); + break; + case FOUND_VC: + handleCryptoSelect(); + break; + case WAIT_FOR_PAD_D: + handlePadD(); + break; + case NORMAL_HANDSHAKE: + // let AuthenticateBase deal with the data + AuthenticateBase::onReadyRead(); + break; + }; + } + + +} + +#include "encryptedauthenticate.moc" diff --git a/libktorrent/mse/encryptedauthenticate.h b/libktorrent/mse/encryptedauthenticate.h new file mode 100644 index 0000000..74ccc1b --- /dev/null +++ b/libktorrent/mse/encryptedauthenticate.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2005 by Joris Guisson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef MSEENCRYPTEDAUTHENTICATE_H +#define MSEENCRYPTEDAUTHENTICATE_H + +#include <util/sha1hash.h> +#include <torrent/authenticate.h> +#include "bigint.h" + + +namespace mse +{ + class RC4Encryptor; + + const Uint32 MAX_EA_BUF_SIZE = 622 + 512; + + /** + * @author Joris Guisson <[email protected]> + * + * Encrypted version of the Authenticate class + */ + class EncryptedAuthenticate : public bt::Authenticate + { + Q_OBJECT + public: + EncryptedAuthenticate(const QString& ip, Uint16 port, const bt::SHA1Hash& info_hash, const bt::PeerID& peer_id, bt::PeerManager* pman); + virtual ~EncryptedAuthenticate(); + + private slots: + virtual void connected(); + virtual void onReadyRead(); + + private: + void handleYB(); + void handleCryptoSelect(); + void findVC(); + void handlePadD(); + + private: + enum State + { + NOT_CONNECTED, + SENT_YA, + GOT_YB, + FOUND_VC, + WAIT_FOR_PAD_D, + NORMAL_HANDSHAKE + }; + + BigInt xa,ya,s,skey,yb; + State state; + RC4Encryptor* our_rc4; + Uint8 buf[MAX_EA_BUF_SIZE]; + Uint32 buf_size; + Uint32 vc_off; + Uint32 dec_bytes; + bt::SHA1Hash enc,dec; + Uint32 crypto_select; + Uint16 pad_D_len; + Uint32 end_of_crypto_handshake; + }; + +} + +#endif diff --git a/libktorrent/mse/encryptedserverauthenticate.cpp b/libktorrent/mse/encryptedserverauthenticate.cpp new file mode 100644 index 0000000..40353ad --- /dev/null +++ b/libktorrent/mse/encryptedserverauthenticate.cpp @@ -0,0 +1,354 @@ +/*************************************************************************** + * Copyright (C) 2005 by Joris Guisson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include <stdlib.h> +#include <util/functions.h> +#include <util/log.h> +#include <torrent/server.h> +#include <torrent/globals.h> +#include "encryptedserverauthenticate.h" +#include "functions.h" +#include "streamsocket.h" +#include "rc4encryptor.h" + +using namespace bt; + +namespace mse +{ + EncryptedServerAuthenticate::EncryptedServerAuthenticate(mse::StreamSocket* sock, bt::Server* server): bt::ServerAuthenticate(sock, server) + { + mse::GeneratePublicPrivateKey(xb,yb); + state = WAITING_FOR_YA; + buf_size = 0; + req1_off = 0; + our_rc4 = 0; + pad_C_len = 0; + crypto_provide = crypto_select = 0; + } + + + EncryptedServerAuthenticate::~EncryptedServerAuthenticate() + { + delete our_rc4; + } + + void EncryptedServerAuthenticate::sendYB() + { + Uint8 tmp[608]; + yb.toBuffer(tmp,96); + // DumpBigInt("Xb",xb); + // DumpBigInt("Yb",yb); + sock->sendData(tmp,96 + rand() % 512); + //Out() << "Sent YB" << endl; + } + + + void EncryptedServerAuthenticate::handleYA() + { + sendYB(); + + ya = BigInt::fromBuffer(buf,96); + // DumpBigInt("Ya",ya); + // now calculate secret + s = mse::DHSecret(xb,ya); + // DumpBigInt("S",s); + state = WAITING_FOR_REQ1; + // see if we can find req1 + findReq1(); + } + + void EncryptedServerAuthenticate::findReq1() + { + if (buf_size < 116) // safety check + return; + + // Out() << "Find Req1" << endl; + Uint8 tmp[100]; + memcpy(tmp,"req1",4); + s.toBuffer(tmp + 4,96); + SHA1Hash req1 = SHA1Hash::generate(tmp,100); + for (Uint32 i = 96;i < buf_size - 20;i++) + { + if (buf[i] == req1.getData()[0] && memcmp(buf+i,req1.getData(),20) == 0) + { + state = FOUND_REQ1; + req1_off = i; + calculateSKey(); + return; + } + } + + if (buf_size > 608) + { + // Out(SYS_CON|LOG_DEBUG) << "Couldn't find req1" << endl; + onFinish(false); + } + } + + void EncryptedServerAuthenticate::calculateSKey() + { + // Out(SYS_CON|LOG_DEBUG) << "Calculate SKEY" << endl; + // not enough data return + if (req1_off + 40 > buf_size) + return; + + Uint8 tmp[100]; + memcpy(tmp,"req3",4); + s.toBuffer(tmp+4,96); + SHA1Hash r3 = SHA1Hash::generate(tmp,100); + SHA1Hash r(buf + req1_off + 20); + + // r = HASH('req2', SKEY) xor HASH('req3', S) + SHA1Hash r2 = r ^ r3; // now calculate HASH('req2', SKEY) + if (!server->findInfoHash(r2,info_hash)) + { + // Out(SYS_CON|LOG_DEBUG) << "Unknown info_hash" << endl; + onFinish(false); + return; + } + // we have found the info_hash, now process VC and the rest + state = FOUND_INFO_HASH; + processVC(); + } + + void EncryptedServerAuthenticate::processVC() + { + // Out(SYS_CON|LOG_DEBUG) << "Process VC" << endl; + if (!our_rc4) + { + // calculate the keys + SHA1Hash enc = mse::EncryptionKey(false,s,info_hash); + SHA1Hash dec = mse::EncryptionKey(true,s,info_hash); + //Out() << "enc = " << enc.toString() << endl; + //Out() << "dec = " << dec.toString() << endl; + our_rc4 = new RC4Encryptor(dec,enc); + } + + // if we do not have everything return + if (buf_size < req1_off + 40 + 14) + return; + + + Uint32 off = req1_off + 40; + // now decrypt the vc and crypto_provide and the length of pad_C + our_rc4->decrypt(buf + off,14); + + // check the VC + for (Uint32 i = 0;i < 8;i++) + { + if (buf[off + i]) + { + // Out(SYS_CON|LOG_DEBUG) << "Illegal VC" << endl; + onFinish(false); + return; + } + } + // get crypto_provide and the length of pad_C + crypto_provide = bt::ReadUint32(buf,off + 8); + pad_C_len = bt::ReadUint16(buf,off + 12); + if (pad_C_len > 512) + { + Out(SYS_CON|LOG_DEBUG) << "Illegal pad C length" << endl; + onFinish(false); + return; + } + + // now we have crypto_provide we can send + // ENCRYPT(VC, crypto_select, len(padD), padD) + Uint8 tmp[14]; + memset(tmp,0,14); // VC + if (crypto_provide & 0x0000002) // RC4 + { + WriteUint32(tmp,8,0x0000002); + crypto_select = 0x0000002; + } + else + { + WriteUint32(tmp,8,0x0000001); + crypto_select = 0x0000001; + } + bt::WriteUint16(tmp,12,0); // no pad D + + sock->sendData(our_rc4->encrypt(tmp,14),14); + + // handle pad C + if (buf_size < req1_off + 14 + pad_C_len) + { + // we do not have the full padC + state = WAIT_FOR_PAD_C; + return; + } + + handlePadC(); + } + + void EncryptedServerAuthenticate::handlePadC() + { + // Out(SYS_CON|LOG_DEBUG) << "Handle PAD C" << endl; + // not enough data, so return, we need padC and the length of IA + if (buf_size < req1_off + 54 + pad_C_len + 2) + return; + + // we have decrypted everything up to pad_C_len + Uint32 off = req1_off + 54; + our_rc4->decrypt(buf + off,pad_C_len + 2); + ia_len = bt::ReadUint16(buf,off + pad_C_len); + if (buf_size < off + ia_len) + { + // we do not have the IA, so wait for it + state = WAIT_FOR_IA; + return; + } + handleIA(); + } + + void EncryptedServerAuthenticate::handleIA() + { + // Out(SYS_CON|LOG_DEBUG) << "Handle IA" << endl; + // not enough data, so return, we need padC and the length of IA + if (buf_size < req1_off + 54 + pad_C_len + 2 + ia_len) + return; + + // decrypt the initial argument + if (ia_len > 0) + { + Uint32 off = req1_off + 54 + pad_C_len + 2; + // reinsert everything so that the normal authentication can handle it + sock->reinsert(buf + off,buf_size - off); + } + + bool allow_unenc = Globals::instance().getServer().unencryptedConnectionsAllowed(); + + if (crypto_select & 0x0000002) + { + sock->setRC4Encryptor(our_rc4); + our_rc4 = 0; + } + else if (!allow_unenc && crypto_select & 0x00000001) + { + // if no encrypted connections + Out(SYS_CON|LOG_DEBUG) << "Unencrypted connections not allowed" << endl; + onFinish(false); + return; + } + else + { + delete our_rc4; + our_rc4 = 0; + } + + // hand it over to ServerAuthenticate + state = NON_ENCRYPTED_HANDSHAKE; + ServerAuthenticate::onReadyRead(); + } + + void EncryptedServerAuthenticate::onReadyRead() + { + if (!sock) + return; + + Uint32 ba = sock->bytesAvailable(); + if (!ba) + { + onFinish(false); + return; + } + + // make sure we don't write past the end of the buffer + if (buf_size + ba > MAX_SEA_BUF_SIZE) + ba = MAX_SEA_BUF_SIZE - buf_size; + + switch (state) + { + case WAITING_FOR_YA: + if (ba <= 68 && Globals::instance().getServer().unencryptedConnectionsAllowed()) + { + // this is most likely an unencrypted handshake, so if we can find a peer manager + // for the info hash in it, add it to the list of potential peers of that peer manager + // so it will be contacted later on + /* buf_size += sock->readData(buf + buf_size,ba); + if (buf_size >= 48) + { + SHA1Hash rh(buf+28); + PeerManager* pman = server->findPeerManager(rh); + if (pman) + { + PotentialPeer pp; + pp.ip = sock->getRemoteIPAddress(); + pp.port = sock->getRemotePort(); + pman->addPotentialPeer(pp); + } + } + onFinish(false); + */ + Out(SYS_CON|LOG_DEBUG) << "Switching back to normal server authenticate" << endl; + state = NON_ENCRYPTED_HANDSHAKE; + ServerAuthenticate::onReadyRead(); + } + else + { + buf_size += sock->readData(buf + buf_size,ba); + if (buf_size >= 96) + handleYA(); + } + break; + case WAITING_FOR_REQ1: + if (buf_size + ba > MAX_SEA_BUF_SIZE) + ba = MAX_SEA_BUF_SIZE - buf_size; + + buf_size += sock->readData(buf + buf_size,ba); + findReq1(); + break; + case FOUND_REQ1: + if (buf_size + ba > MAX_SEA_BUF_SIZE) + ba = MAX_SEA_BUF_SIZE - buf_size; + + buf_size += sock->readData(buf + buf_size,ba); + calculateSKey(); + break; + case FOUND_INFO_HASH: + if (buf_size + ba > MAX_SEA_BUF_SIZE) + ba = MAX_SEA_BUF_SIZE - buf_size; + + buf_size += sock->readData(buf + buf_size,ba); + processVC(); + break; + case WAIT_FOR_PAD_C: + if (buf_size + ba > MAX_SEA_BUF_SIZE) + ba = MAX_SEA_BUF_SIZE - buf_size; + + buf_size += sock->readData(buf + buf_size,ba); + handlePadC(); + break; + case WAIT_FOR_IA: + if (buf_size + ba > MAX_SEA_BUF_SIZE) + ba = MAX_SEA_BUF_SIZE - buf_size; + + buf_size += sock->readData(buf + buf_size,ba); + handleIA(); + break; + case NON_ENCRYPTED_HANDSHAKE: + ServerAuthenticate::onReadyRead(); + break; + } + } + + +} +#include "encryptedserverauthenticate.moc" diff --git a/libktorrent/mse/encryptedserverauthenticate.h b/libktorrent/mse/encryptedserverauthenticate.h new file mode 100644 index 0000000..3c358cd --- /dev/null +++ b/libktorrent/mse/encryptedserverauthenticate.h @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright (C) 2005 by Joris Guisson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef MSEENCRYPTEDSERVERAUTHENTICATE_H +#define MSEENCRYPTEDSERVERAUTHENTICATE_H + +#include <util/sha1hash.h> +#include <torrent/serverauthenticate.h> +#include "bigint.h" + +namespace mse +{ + class RC4Encryptor; + + + const Uint32 MAX_SEA_BUF_SIZE = 608 + 20 + 20 + 8 + 4 + 2 + 512 + 2 + 68; + /** + @author Joris Guisson <[email protected]> + */ + class EncryptedServerAuthenticate : public bt::ServerAuthenticate + { + Q_OBJECT + public: + EncryptedServerAuthenticate(mse::StreamSocket* sock, bt::Server* server); + virtual ~EncryptedServerAuthenticate(); + + private slots: + virtual void onReadyRead(); + + private: + void handleYA(); + void sendYB(); + void findReq1(); + void calculateSKey(); + void processVC(); + void handlePadC(); + void handleIA(); + + private: + enum State + { + WAITING_FOR_YA, + WAITING_FOR_REQ1, + FOUND_REQ1, + FOUND_INFO_HASH, + WAIT_FOR_PAD_C, + WAIT_FOR_IA, + NON_ENCRYPTED_HANDSHAKE + }; + BigInt xb,yb,s,ya; + bt::SHA1Hash skey,info_hash; + State state; + Uint8 buf[MAX_SEA_BUF_SIZE]; + Uint32 buf_size; + Uint32 req1_off; + Uint32 crypto_provide,crypto_select; + Uint16 pad_C_len; + Uint16 ia_len; + RC4Encryptor* our_rc4; + }; + +} + +#endif diff --git a/libktorrent/mse/functions.cpp b/libktorrent/mse/functions.cpp new file mode 100644 index 0000000..bb19b93 --- /dev/null +++ b/libktorrent/mse/functions.cpp @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (C) 2005 by Joris Guisson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include <util/log.h> +#include <torrent/globals.h> +#include <util/sha1hash.h> +#include "functions.h" +#include "bigint.h" + +using namespace bt; + +namespace mse +{ + /* + static const BigInt P = BigInt( + "0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD" + "129024E088A67CC74020BBEA63B139B22514A08798E3404" + "DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C" + "245E485B576625E7EC6F44C42E9A63A36210000000000090563"); + */ + static const BigInt P = BigInt("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563"); + + void GeneratePublicPrivateKey(BigInt & priv,BigInt & pub) + { + BigInt G = BigInt("0x02"); + priv = BigInt::random(); + pub = BigInt::powerMod(G,priv,P); + } + + BigInt DHSecret(const BigInt & our_priv,const BigInt & peer_pub) + { + return BigInt::powerMod(peer_pub,our_priv,P); + } + + bt::SHA1Hash EncryptionKey(bool a,const BigInt & s,const bt::SHA1Hash & skey) + { + Uint8 buf[120]; + memcpy(buf,"key",3); + buf[3] = (Uint8)(a ? 'A' : 'B'); + s.toBuffer(buf + 4,96); + memcpy(buf + 100,skey.getData(),20); + return bt::SHA1Hash::generate(buf,120); + } + + void DumpBigInt(const QString & name,const BigInt & bi) + { + static Uint8 buf[512]; + Uint32 nb = bi.toBuffer(buf,512); + bt::Log & lg = Out(); + lg << name << " (" << nb << ") = "; + for (Uint32 i = 0;i < nb;i++) + { + lg << QString("0x%1 ").arg(buf[i],0,16); + } + lg << endl; + } + +} diff --git a/libktorrent/mse/functions.h b/libktorrent/mse/functions.h new file mode 100644 index 0000000..4be1667 --- /dev/null +++ b/libktorrent/mse/functions.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2005 by Joris Guisson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef MSEFUNCTIONS_H +#define MSEFUNCTIONS_H + +namespace bt +{ + class SHA1Hash; +} + +namespace mse +{ + class BigInt; + + void GeneratePublicPrivateKey(BigInt & pub,BigInt & priv); + BigInt DHSecret(const BigInt & our_priv,const BigInt & peer_pub); + bt::SHA1Hash EncryptionKey(bool a,const BigInt & s,const bt::SHA1Hash & skey); + + void DumpBigInt(const QString & name,const BigInt & bi); +} + +#endif diff --git a/libktorrent/mse/rc4encryptor.cpp b/libktorrent/mse/rc4encryptor.cpp new file mode 100644 index 0000000..422fe5d --- /dev/null +++ b/libktorrent/mse/rc4encryptor.cpp @@ -0,0 +1,100 @@ + /*************************************************************************** + * Copyright (C) 2005 by Joris Guisson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include "rc4encryptor.h" + +namespace mse +{ + static void swap(Uint8 & a,Uint8 & b) + { + Uint8 tmp = a; + a = b; + b = tmp; + } + + static Uint8 rc4_enc_buffer[bt::MAX_MSGLEN]; + + RC4::RC4(const Uint8* key,Uint32 size) : i(0),j(0) + { + // initialize state + for (Uint32 t = 0;t < 256;t++) + s[t] = t; + + j = 0; + for (Uint32 t=0;t < 256;t++) + { + j = (j + s[t] + key[t % size]) & 0xff; + swap(s[t],s[j]); + } + + i = j = 0; + } + + RC4::~RC4() + { + } + + void RC4::process(const Uint8* in,Uint8* out,Uint32 size) + { + for (Uint32 k = 0;k < size;k++) + { + out[k] = process(in[k]); + } + } + + Uint8 RC4::process(Uint8 b) + { + i = (i + 1) & 0xff; + j = (j + s[i]) & 0xff; + swap(s[i],s[j]); + Uint8 tmp = s[ (s[i] + s[j]) & 0xff]; + return tmp ^ b; + } + + + RC4Encryptor::RC4Encryptor(const bt::SHA1Hash & dk,const bt::SHA1Hash & ek) + : enc(ek.getData(),20),dec(dk.getData(),20) + { + Uint8 tmp[1024]; + enc.process(tmp,tmp,1024); + dec.process(tmp,tmp,1024); + } + + + RC4Encryptor::~RC4Encryptor() + {} + + + void RC4Encryptor::decrypt(Uint8* data,Uint32 len) + { + dec.process(data,data,len); + } + + const Uint8* RC4Encryptor::encrypt(const Uint8* data,Uint32 len) + { + enc.process(data,rc4_enc_buffer,len); + return rc4_enc_buffer; + } + + void RC4Encryptor::encryptReplace(Uint8* data,Uint32 len) + { + enc.process(data,data,len); + } + +} diff --git a/libktorrent/mse/rc4encryptor.h b/libktorrent/mse/rc4encryptor.h new file mode 100644 index 0000000..650b54e --- /dev/null +++ b/libktorrent/mse/rc4encryptor.h @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2005 by Joris Guisson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef MSERC4ENCRYPTOR_H +#define MSERC4ENCRYPTOR_H + +#include <util/sha1hash.h> +#include <util/constants.h> + +using bt::Uint8; +using bt::Uint32; + +namespace mse +{ + /** + * Helper class to do the actual encryption / decryption + */ + class RC4 + { + Uint8 i,j; + Uint8 s[256]; + public: + RC4(const Uint8* key,Uint32 size); + virtual ~RC4(); + + void process(const Uint8* in,Uint8* out,Uint32 size); + Uint8 process(Uint8 b); + }; + + /** + * @author Joris Guisson <[email protected]> + * + * RC4 encryptor. Uses the RC4 algorithm to encrypt and decrypt data. + * This class has a static encryption buffer, which makes it not thread safe + * because the buffer is not protected by mutexes. + */ + class RC4Encryptor + { + RC4 enc,dec; + public: + RC4Encryptor(const bt::SHA1Hash & dkey,const bt::SHA1Hash & ekey); + virtual ~RC4Encryptor(); + + /** + * Decrypt some data, decryption happens in place (original data gets overwritten) + * @param data The data + * @param len Size of the data + */ + void decrypt(Uint8* data,Uint32 len); + + /** + * Encrypt the data. Encryption happens into the static buffer. + * So that the data passed to this function is never overwritten. + * If we send pieces we point directly to the mmap region of data, + * this cannot be overwritten, hence the static buffer. + * @param data The data + * @param len The length of the data + * @return Pointer to the static buffer + */ + const Uint8* encrypt(const Uint8* data,Uint32 len); + + /** + * Encrypt data, encryption will happen in the same buffer. So data will + * be changed replaced by it's encrypted version. + * @param data The data to encrypt + * @param len The length of the data + */ + void encryptReplace(Uint8* data,Uint32 len); + + /** + * Encrypts one byte. + * @param b The byte to encrypt + * @return The encrypted byte + */ + Uint8 encrypt(Uint8 b) {return enc.process(b);} + }; + +} + +#endif diff --git a/libktorrent/mse/streamsocket.cpp b/libktorrent/mse/streamsocket.cpp new file mode 100644 index 0000000..19a0a2e --- /dev/null +++ b/libktorrent/mse/streamsocket.cpp @@ -0,0 +1,326 @@ +/*************************************************************************** + * Copyright (C) 2005 by Joris Guisson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include <errno.h> +#include <qsocket.h> +#include <qsocketdevice.h> +#include <util/sha1hash.h> +#include <util/log.h> +#include <torrent/peer.h> +#include <torrent/globals.h> +#include <torrent/authenticatebase.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <net/socketmonitor.h> +#include "streamsocket.h" +#include "rc4encryptor.h" + +using namespace bt; +using namespace net; + +namespace mse +{ + + Uint8 StreamSocket::tos = IPTOS_THROUGHPUT; + Uint32 StreamSocket::num_connecting = 0; + Uint32 StreamSocket::max_connecting = 50; + + StreamSocket::StreamSocket() : sock(0),enc(0),monitored(false) + { + sock = new BufferedSocket(true); + sock->setNonBlocking(); + reinserted_data = 0; + reinserted_data_size = 0; + reinserted_data_read = 0; + + } + + StreamSocket::StreamSocket(int fd) : sock(0),enc(0),monitored(false) + { + sock = new BufferedSocket(fd); + sock->setNonBlocking(); + reinserted_data = 0; + reinserted_data_size = 0; + reinserted_data_read = 0; + sock->setTOS(tos); + } + + StreamSocket::~StreamSocket() + { + // make sure the number of connecting sockets is updated + if (connecting() && num_connecting > 0) + num_connecting--; + + SocketMonitor::instance().remove(sock); + delete [] reinserted_data; + delete enc; + delete sock; + } + + void StreamSocket::startMonitoring(net::SocketReader* rdr,net::SocketWriter* wrt) + { + this->rdr = rdr; + this->wrt = wrt; + sock->setReader(this); + sock->setWriter(this); + SocketMonitor::instance().add(sock); + monitored = true; + if (reinserted_data) + { + if (enc) + enc->decrypt(reinserted_data + reinserted_data_read, + reinserted_data_size - reinserted_data_read); + + rdr->onDataReady(reinserted_data + reinserted_data_read, + reinserted_data_size - reinserted_data_read); + delete [] reinserted_data; + reinserted_data = 0; + reinserted_data_size = 0; + } + } + + + Uint32 StreamSocket::sendData(const Uint8* data,Uint32 len) + { + if (enc) + { + // we need to make sure all data is sent because of the encryption + Uint32 ds = 0; + const Uint8* ed = enc->encrypt(data,len); + while (sock->ok() && ds < len) + { + Uint32 ret = sock->send(ed + ds,len - ds); + ds += ret; + if (ret == 0) + { + Out(SYS_CON|LOG_DEBUG) << "ret = 0" << endl; + } + } + if (ds != len) + Out() << "ds != len" << endl; + return ds; + } + else + { + Uint32 ret = sock->send(data,len); + if (ret != len) + Out() << "ret != len" << endl; + return ret; + } + } + + Uint32 StreamSocket::readData(Uint8* buf,Uint32 len) + { + Uint32 ret2 = 0; + if (reinserted_data) + { + Uint32 tr = reinserted_data_size - reinserted_data_read; + if (tr < len) + { + memcpy(buf,reinserted_data + reinserted_data_read,tr); + delete [] reinserted_data; + reinserted_data = 0; + reinserted_data_size = reinserted_data_read = 0; + ret2 = tr; + if (enc) + enc->decrypt(buf,tr); + } + else + { + tr = len; + memcpy(buf,reinserted_data + reinserted_data_read,tr); + reinserted_data_read += tr; + if (enc) + enc->decrypt(buf,tr); + return tr; + } + } + + if (len == ret2) + return ret2; + + Uint32 ret = sock->recv(buf + ret2,len - ret2); + if (ret + ret2 > 0 && enc) + enc->decrypt(buf,ret + ret2); + + return ret; + } + + Uint32 StreamSocket::bytesAvailable() const + { + Uint32 ba = sock->bytesAvailable(); + if (reinserted_data_size - reinserted_data_read > 0) + return ba + (reinserted_data_size - reinserted_data_read); + else + return ba; + } + + void StreamSocket::close() + { + sock->close(); + } + + bool StreamSocket::connectTo(const QString & ip,Uint16 port) + { + // do a safety check + if (ip.isNull() || ip.length() == 0) + return false; + + // we don't wanna block the current thread so set non blocking + sock->setNonBlocking(); + if (sock->connectTo(Address(ip,port))) + { + sock->setTOS(tos); + return true; + } + else if (connecting()) + { + num_connecting++; + } + + return false; + } + + void StreamSocket::initCrypt(const bt::SHA1Hash & dkey,const bt::SHA1Hash & ekey) + { + if (enc) + delete enc; + + enc = new RC4Encryptor(dkey,ekey); + } + + void StreamSocket::disableCrypt() + { + delete enc; + enc = 0; + } + + bool StreamSocket::ok() const + { + return sock->ok(); + } + + QString StreamSocket::getRemoteIPAddress() const + { + return sock->getPeerName().toString(); + } + + bt::Uint16 StreamSocket::getRemotePort() const + { + return sock->getPeerName().port(); + } + + net::Address StreamSocket::getRemoteAddress() const + { + return sock->getPeerName(); + } + + void StreamSocket::setRC4Encryptor(RC4Encryptor* e) + { + if (enc) + delete enc; + + enc = e; + } + + void StreamSocket::reinsert(const Uint8* d,Uint32 size) + { +// Out() << "Reinsert : " << size << endl; + Uint32 off = 0; + if (reinserted_data) + { + off = reinserted_data_size; + reinserted_data = (Uint8*)realloc(reinserted_data,reinserted_data_size + size); + reinserted_data_size += size; + } + else + { + reinserted_data = new Uint8[size]; + reinserted_data_size = size; + } + memcpy(reinserted_data + off,d,size); + } + + bool StreamSocket::connecting() const + { + return sock->state() == net::Socket::CONNECTING; + } + + void StreamSocket::onDataReady(Uint8* buf,Uint32 size) + { + if (enc) + enc->decrypt(buf,size); + + if (rdr) + rdr->onDataReady(buf,size); + } + + Uint32 StreamSocket::onReadyToWrite(Uint8* data,Uint32 max_to_write) + { + if (!wrt) + return 0; + + Uint32 ret = wrt->onReadyToWrite(data,max_to_write); + if (enc && ret > 0) // do encryption if necessary + enc->encryptReplace(data,ret); + + + return ret; + } + + bool StreamSocket::hasBytesToWrite() const + { + return wrt ? wrt->hasBytesToWrite() : false; + } + + float StreamSocket::getDownloadRate() const + { + return sock ? sock->getDownloadRate() : 0.0f; + } + + float StreamSocket::getUploadRate() const + { + return sock ? sock->getUploadRate() : 0.0f; + } + + bool StreamSocket::connectSuccesFull() const + { + bool ret = sock->connectSuccesFull(); + if (ret) + sock->setTOS(tos); + + if (num_connecting > 0) + num_connecting--; + + return ret; + } + + void StreamSocket::setGroupIDs(Uint32 up,Uint32 down) + { + sock->setGroupID(up,true); + sock->setGroupID(down,false); + } +} + +#include "streamsocket.moc" diff --git a/libktorrent/mse/streamsocket.h b/libktorrent/mse/streamsocket.h new file mode 100644 index 0000000..5006a7b --- /dev/null +++ b/libktorrent/mse/streamsocket.h @@ -0,0 +1,185 @@ +/*************************************************************************** + * Copyright (C) 2005 by Joris Guisson * + * [email protected] * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef MSESTREAMSOCKET_H +#define MSESTREAMSOCKET_H + +#include <qobject.h> +#include <util/constants.h> +#include <net/bufferedsocket.h> + +class QString; + +using bt::Uint8; +using bt::Uint16; +using bt::Uint32; + +namespace bt +{ + class SHA1Hash; + class Peer; + class AuthenticateBase; +} + +namespace mse +{ + class RC4Encryptor; + + + + + /** + * @author Joris Guisson <[email protected]> + * + * Wrapper around a TCP socket which handles RC4 encryption. + * Once authentication is done, the sendData and readData interfaces should + * not be used anymore, a SocketReader and SocketWriter should be provided, + * so that reading and writing is controlled from the monitor thread. + */ + class StreamSocket : public QObject,public net::SocketReader,public net::SocketWriter + { + Q_OBJECT + public: + StreamSocket(); + StreamSocket(int fd); + virtual ~StreamSocket(); + + /** + * Send a chunk of data. (Does not encrypt the data) + * @param data The data + * @param len The length + * @return Number of bytes written + */ + Uint32 sendData(const Uint8* data,Uint32 len); + + /** + * Reads data from the peer. + * @param buf The buffer to store the data + * @param len The maximum number of bytes to read + * @return The number of bytes read + */ + Uint32 readData(Uint8* buf,Uint32 len); + + /// Get the number of bytes available to read. + Uint32 bytesAvailable() const; + + /// Are we using encryption + bool encrypted() const {return enc != 0;} + + /** + * Initialize the RC4 encryption algorithm. + * @param dkey + * @param ekey + */ + void initCrypt(const bt::SHA1Hash & dkey,const bt::SHA1Hash & ekey); + + /// Set the encryptor + void setRC4Encryptor(RC4Encryptor* enc); + + /// Disables encryption. All data will be sent over as plain text. + void disableCrypt(); + + /// Close the socket + void close(); + + /// Connect the socket to a remote host + bool connectTo(const QString & ip,Uint16 port); + + /// Get the IP address of the remote peer + QString getRemoteIPAddress() const; + + /// Get the port of the remote peer + bt::Uint16 getRemotePort() const; + + /// Get the full address + net::Address getRemoteAddress() const; + + /** + * Reinsert data, this is needed when we read to much during the crypto handshake. + * This data will be the first to read out. The data will be copied to a temporary buffer + * which will be destroyed when the reinserted data has been read. + */ + void reinsert(const Uint8* d,Uint32 size); + + /// see if the socket is still OK + bool ok() const; + + /// Get the file descriptor + int fd() const {return sock->fd();} + + /// Start monitoring of this socket by the monitor thread + void startMonitoring(net::SocketReader* rdr,net::SocketWriter* wrt); + + /// Is this socket connecting to a remote host + bool connecting() const; + + /// See if a connect was success full + bool connectSuccesFull() const; + + /// Get the current download rate + float getDownloadRate() const; + + /// Get the current download rate + float getUploadRate() const; + + /** + * Set the TOS byte for new sockets. + * @param t TOS value + */ + static void setTOS(Uint8 t) {tos = t;} + + /** + * Set the download and upload group ID's + * @param up Upload group ID + * @param down Download group ID + */ + void setGroupIDs(Uint32 up,Uint32 down); + + /** + * Check if we are allowed to initiate another outgoing connection. + */ + static bool canInitiateNewConnection() {return num_connecting < max_connecting;} + + /** + * Set the maximum number of connecting sockets we are allowed to have. + */ + static void setMaxConnecting(Uint32 mc) {max_connecting = mc;} + private: + virtual void onDataReady(Uint8* buf,Uint32 size); + virtual Uint32 onReadyToWrite(Uint8* data,Uint32 max_to_write); + virtual bool hasBytesToWrite() const; + + private: + net::BufferedSocket* sock; + RC4Encryptor* enc; + Uint8* reinserted_data; + Uint32 reinserted_data_size; + Uint32 reinserted_data_read; + bool monitored; + net::SocketReader* rdr; + net::SocketWriter* wrt; + + static Uint8 tos; + static Uint32 num_connecting; // the number of connections we have in SYN_SENT state + static Uint32 max_connecting; + }; + +} + +#endif |