You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
303 lines
7.7 KiB
303 lines
7.7 KiB
/***************************************************************************
|
|
* Copyright (C) 2005 by Joris Guisson *
|
|
* joris.guisson@gmail.com *
|
|
* *
|
|
* 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 TQString& 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"
|