From c0d2223c778ae9719fd13d1fde6dc2fbf4faa82b Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 17 Feb 2008 15:56:47 +0000 Subject: [PATCH] 2008-02-16 Tatsuhiro Tsujikawa Added Message Stream Encryption(MSE) support. Currently, aria2 accepts incoming connections with Obfuscation Header and legacy BitTorrent Header and establishes connections with Obfuscation Header first and if failed then retry with legacy BitTorrent header. If plain text and ARC4 is provided, aria2 always choose ARC4. The new option to change the default behavior is planned. For tracker extension, "supportcrypto=1" is added statically. * src/PeerInitiateConnectionCommand.{h, cc} * src/PeerConnection.{h, cc} * src/HandleRegistry.h * src/SocketCore.h * src/PeerReceiveHandshakeCommand.{h, cc} * src/BtRegistry.{h, cc} * src/PeerListenCommand.cc * src/InitiatorMSEHandshakeCommand.{h, cc} * src/ReceiverMSEHandshakeCommand.{h, cc} * src/MSEHandshake.{h, cc} * src/ARC4Encryptor.h * src/ARC4Decryptor.h * src/LibgcryptARC4Encryptor.h * src/LibgcryptARC4Decryptor.h * src/LibgcryptARC4Context.h * src/LibsslARC4Encryptor.h * src/LibsslARC4Decryptor.h * src/LibsslARC4Context.h * src/DHKeyExchange.h * src/LibgcryptDHKeyExchange.h * src/LibsslDHKeyExchange.h * src/DefaultBtAnnounce.cc: Just added "supportcrypto=1" parameter. * test/DefaultBtAnnounceTest.cc * test/ARC4Test.cc * test/DHKeyExchangeTest.cc Removed prepareForRetry() because it is not used. * src/PeerAbstractCommand.{h, cc} * src/PeerInteractionCommand.{h, cc} * src/PeerInitiateConnectionCommand.{h, cc} --- ChangeLog | 40 ++ src/ARC4Decryptor.h | 45 ++ src/ARC4Encryptor.h | 45 ++ src/BtRegistry.cc | 5 + src/BtRegistry.h | 2 + src/DHKeyExchange.h | 45 ++ src/DefaultBtAnnounce.cc | 1 + src/HandleRegistry.h | 11 + src/InitiatorMSEHandshakeCommand.cc | 165 ++++++++ src/InitiatorMSEHandshakeCommand.h | 80 ++++ src/LibgcryptARC4Context.h | 96 +++++ src/LibgcryptARC4Decryptor.h | 77 ++++ src/LibgcryptARC4Encryptor.h | 77 ++++ src/LibgcryptDHKeyExchange.h | 164 ++++++++ src/LibsslARC4Context.h | 96 +++++ src/LibsslARC4Decryptor.h | 77 ++++ src/LibsslARC4Encryptor.h | 77 ++++ src/LibsslDHKeyExchange.h | 171 ++++++++ src/MSEHandshake.cc | 591 +++++++++++++++++++++++++++ src/MSEHandshake.h | 179 ++++++++ src/Makefile.am | 5 +- src/Makefile.in | 36 +- src/PeerAbstractCommand.cc | 4 - src/PeerAbstractCommand.h | 1 - src/PeerConnection.cc | 64 ++- src/PeerConnection.h | 15 + src/PeerInitiateConnectionCommand.cc | 47 +-- src/PeerInitiateConnectionCommand.h | 6 +- src/PeerInteractionCommand.cc | 5 - src/PeerInteractionCommand.h | 1 - src/PeerListenCommand.cc | 16 +- src/PeerReceiveHandshakeCommand.cc | 11 +- src/PeerReceiveHandshakeCommand.h | 3 +- src/ReceiverMSEHandshakeCommand.cc | 157 +++++++ src/ReceiverMSEHandshakeCommand.h | 76 ++++ src/SocketCore.h | 14 + test/ARC4Test.cc | 51 +++ test/DHKeyExchangeTest.cc | 52 +++ test/DefaultBtAnnounceTest.cc | 38 +- test/Makefile.am | 4 +- test/Makefile.in | 15 +- 41 files changed, 2569 insertions(+), 96 deletions(-) create mode 100644 src/ARC4Decryptor.h create mode 100644 src/ARC4Encryptor.h create mode 100644 src/DHKeyExchange.h create mode 100644 src/InitiatorMSEHandshakeCommand.cc create mode 100644 src/InitiatorMSEHandshakeCommand.h create mode 100644 src/LibgcryptARC4Context.h create mode 100644 src/LibgcryptARC4Decryptor.h create mode 100644 src/LibgcryptARC4Encryptor.h create mode 100644 src/LibgcryptDHKeyExchange.h create mode 100644 src/LibsslARC4Context.h create mode 100644 src/LibsslARC4Decryptor.h create mode 100644 src/LibsslARC4Encryptor.h create mode 100644 src/LibsslDHKeyExchange.h create mode 100644 src/MSEHandshake.cc create mode 100644 src/MSEHandshake.h create mode 100644 src/ReceiverMSEHandshakeCommand.cc create mode 100644 src/ReceiverMSEHandshakeCommand.h create mode 100644 test/ARC4Test.cc create mode 100644 test/DHKeyExchangeTest.cc diff --git a/ChangeLog b/ChangeLog index 3625f166..e0c86091 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,43 @@ +2008-02-16 Tatsuhiro Tsujikawa + + Added Message Stream Encryption(MSE) support. + Currently, aria2 accepts incoming connections with Obfuscation Header + and legacy BitTorrent Header and establishes connections with + Obfuscation Header first and if failed then retry with legacy + BitTorrent header. If plain text and ARC4 is provided, aria2 always + choose ARC4. The new option to change the default behavior is planned. + For tracker extension, "supportcrypto=1" is added statically. + * src/PeerInitiateConnectionCommand.{h, cc} + * src/PeerConnection.{h, cc} + * src/HandleRegistry.h + * src/SocketCore.h + * src/PeerReceiveHandshakeCommand.{h, cc} + * src/BtRegistry.{h, cc} + * src/PeerListenCommand.cc + * src/InitiatorMSEHandshakeCommand.{h, cc} + * src/ReceiverMSEHandshakeCommand.{h, cc} + * src/MSEHandshake.{h, cc} + * src/ARC4Encryptor.h + * src/ARC4Decryptor.h + * src/LibgcryptARC4Encryptor.h + * src/LibgcryptARC4Decryptor.h + * src/LibgcryptARC4Context.h + * src/LibsslARC4Encryptor.h + * src/LibsslARC4Decryptor.h + * src/LibsslARC4Context.h + * src/DHKeyExchange.h + * src/LibgcryptDHKeyExchange.h + * src/LibsslDHKeyExchange.h + * src/DefaultBtAnnounce.cc: Just added "supportcrypto=1" parameter. + * test/DefaultBtAnnounceTest.cc + * test/ARC4Test.cc + * test/DHKeyExchangeTest.cc + + Removed prepareForRetry() because it is not used. + * src/PeerAbstractCommand.{h, cc} + * src/PeerInteractionCommand.{h, cc} + * src/PeerInitiateConnectionCommand.{h, cc} + 2008-02-13 Tatsuhiro Tsujikawa Added the ability to load nodes from torrent file. These nodes are diff --git a/src/ARC4Decryptor.h b/src/ARC4Decryptor.h new file mode 100644 index 00000000..39dc25cf --- /dev/null +++ b/src/ARC4Decryptor.h @@ -0,0 +1,45 @@ +/* */ +#ifndef _D_ARC4_DECRYPTOR_H_ +#define _D_ARC4_DECRYPTOR_H_ + +#include "common.h" +#ifdef HAVE_LIBGCRYPT +# include "LibgcryptARC4Decryptor.h" +#elif HAVE_LIBSSL +# include "LibsslARC4Decryptor.h" +#endif // HAVE_LIBSSL + +#endif // _D_ARC4_DECRYPTOR_H_ diff --git a/src/ARC4Encryptor.h b/src/ARC4Encryptor.h new file mode 100644 index 00000000..1335b493 --- /dev/null +++ b/src/ARC4Encryptor.h @@ -0,0 +1,45 @@ +/* */ +#ifndef _D_ARC4_ENCRYPTOR_H_ +#define _D_ARC4_ENCRYPTOR_H_ + +#include "common.h" +#ifdef HAVE_LIBGCRYPT +# include "LibgcryptARC4Encryptor.h" +#elif HAVE_LIBSSL +# include "LibsslARC4Encryptor.h" +#endif // HAVE_LIBSSL + +#endif // _D_ARC4_ENCRYPTOR_H_ diff --git a/src/BtRegistry.cc b/src/BtRegistry.cc index 86e9c3b2..1a9b334f 100644 --- a/src/BtRegistry.cc +++ b/src/BtRegistry.cc @@ -131,6 +131,11 @@ BtRegistry::registerBtContext(const std::string& key, btContextMap.registerHandle(key, btContext); } +std::deque > BtRegistry::getAllBtContext() +{ + return btContextMap.getAll(); +} + PeerObjectClusterHandle BtRegistry::getPeerObjectCluster(const std::string& key) { diff --git a/src/BtRegistry.h b/src/BtRegistry.h index 70b3f29c..2ef55d47 100644 --- a/src/BtRegistry.h +++ b/src/BtRegistry.h @@ -99,6 +99,8 @@ public: static void registerBtProgressInfoFile(const std::string& key, const SharedHandle& btProgressInfoFile); + static std::deque > getAllBtContext(); + // for PeerObject static PeerObjectClusterHandle getPeerObjectCluster(const std::string& key); diff --git a/src/DHKeyExchange.h b/src/DHKeyExchange.h new file mode 100644 index 00000000..1cd5cf96 --- /dev/null +++ b/src/DHKeyExchange.h @@ -0,0 +1,45 @@ +/* */ +#ifndef _D_DH_KEY_EXCHANGE_H_ +#define _D_DH_KEY_EXCHANGE_H_ + +#include "common.h" +#ifdef HAVE_LIBGCRYPT +# include "LibgcryptDHKeyExchange.h" +#elif HAVE_LIBSSL +# include "LibsslDHKeyExchange.h" +#endif // HAVE_LIBSSL + +#endif // _D_DH_KEY_EXCHANGE_H_ diff --git a/src/DefaultBtAnnounce.cc b/src/DefaultBtAnnounce.cc index 809a65f2..a804a476 100644 --- a/src/DefaultBtAnnounce.cc +++ b/src/DefaultBtAnnounce.cc @@ -160,6 +160,7 @@ std::string DefaultBtAnnounce::getAnnounceUrl() { url += std::string("&")+"trackerid="+Util::torrentUrlencode((const unsigned char*)trackerId.c_str(), trackerId.size()); } + url += "&supportcrypto=1"; return url; } diff --git a/src/HandleRegistry.h b/src/HandleRegistry.h index c0747347..26ddacb3 100644 --- a/src/HandleRegistry.h +++ b/src/HandleRegistry.h @@ -38,6 +38,7 @@ #include "common.h" #include "SharedHandle.h" #include +#include namespace aria2 { @@ -66,6 +67,16 @@ public: } } + std::deque > getAll() + { + std::deque > l; + for(typename HandleMap::const_iterator itr = handleMap.begin(); itr != handleMap.end(); ++itr) { + const typename HandleMap::value_type& p = *itr; + l.push_back(p.second); + } + return l; + } + void clear() { handleMap.clear(); } diff --git a/src/InitiatorMSEHandshakeCommand.cc b/src/InitiatorMSEHandshakeCommand.cc new file mode 100644 index 00000000..79c11a24 --- /dev/null +++ b/src/InitiatorMSEHandshakeCommand.cc @@ -0,0 +1,165 @@ +/* */ +#include "InitiatorMSEHandshakeCommand.h" +#include "PeerInitiateConnectionCommand.h" +#include "PeerInteractionCommand.h" +#include "DownloadEngine.h" +#include "DlAbortEx.h" +#include "message.h" +#include "prefs.h" +#include "CUIDCounter.h" +#include "Socket.h" +#include "Logger.h" +#include "Peer.h" +#include "PeerConnection.h" +#include "BtContext.h" +#include "BtRuntime.h" +#include "PieceStorage.h" +#include "PeerStorage.h" +#include "BtAnnounce.h" +#include "BtProgressInfoFile.h" +#include "Option.h" +#include "MSEHandshake.h" +#include "ARC4Encryptor.h" +#include "ARC4Decryptor.h" + +namespace aria2 { + +InitiatorMSEHandshakeCommand::InitiatorMSEHandshakeCommand +(int32_t cuid, + RequestGroup* requestGroup, + const SharedHandle& p, + DownloadEngine* e, + const SharedHandle& btContext, + const SharedHandle& s): + + PeerAbstractCommand(cuid, p, e, s), + BtContextAwareCommand(btContext), + RequestGroupAware(requestGroup), + _sequence(INITIATOR_SEND_KEY), + _mseHandshake(new MSEHandshake(cuid, socket, e->option)) +{ + disableReadCheckSocket(); + setWriteCheckSocket(socket); + setTimeout(e->option->getAsInt(PREF_PEER_CONNECTION_TIMEOUT)); + + btRuntime->increaseConnections(); +} + +InitiatorMSEHandshakeCommand::~InitiatorMSEHandshakeCommand() +{ + btRuntime->decreaseConnections(); + + delete _mseHandshake; +} + +bool InitiatorMSEHandshakeCommand::executeInternal() { + switch(_sequence) { + case INITIATOR_SEND_KEY: { + if(!socket->isWritable(0)) { + break; + } + disableWriteCheckSocket(); + setReadCheckSocket(socket); + socket->setBlockingMode(); + setTimeout(e->option->getAsInt(PREF_BT_TIMEOUT)); + _mseHandshake->initEncryptionFacility(true); + _mseHandshake->sendPublicKey(); + _sequence = INITIATOR_WAIT_KEY; + break; + } + case INITIATOR_WAIT_KEY: { + if(_mseHandshake->receivePublicKey()) { + _mseHandshake->initCipher(btContext->getInfoHash()); + _mseHandshake->sendInitiatorStep2(); + _sequence = INITIATOR_FIND_VC_MARKER; + } + break; + } + case INITIATOR_FIND_VC_MARKER: { + if(_mseHandshake->findInitiatorVCMarker()) { + _sequence = INITIATOR_RECEIVE_PAD_D_LENGTH; + } + break; + } + case INITIATOR_RECEIVE_PAD_D_LENGTH: { + if(_mseHandshake->receiveInitiatorCryptoSelectAndPadDLength()) { + _sequence = INITIATOR_RECEIVE_PAD_D; + } + break; + } + case INITIATOR_RECEIVE_PAD_D: { + if(_mseHandshake->receivePad()) { + SharedHandle peerConnection = + new PeerConnection(cuid, socket, e->option); + if(_mseHandshake->getNegotiatedCryptoType() == MSEHandshake::CRYPTO_ARC4) { + peerConnection->enableEncryption(_mseHandshake->getEncryptor(), + _mseHandshake->getDecryptor()); + } + Command* c = + new PeerInteractionCommand(cuid, _requestGroup, peer, e, btContext, + socket, + PeerInteractionCommand::INITIATOR_SEND_HANDSHAKE, + peerConnection); + e->commands.push_back(c); + return true; + } + break; + } + } + e->commands.push_back(this); + return false; +} + +bool InitiatorMSEHandshakeCommand::prepareForNextPeer(int32_t wait) +{ + // try legacy BitTorrent handshake if preference allows it. + // TODO preference is not created yet. + logger->info("CUID#%d - Retry using legacy BitTorrent handshake.", cuid); + Command* command = + new PeerInitiateConnectionCommand(cuid, _requestGroup, peer, e, btContext, + false); + e->commands.push_back(command); + return true; +} + +void InitiatorMSEHandshakeCommand::onAbort(Exception* ex) {} + +bool InitiatorMSEHandshakeCommand::exitBeforeExecute() +{ + return btRuntime->isHalt(); +} + +} // namespace aria2 diff --git a/src/InitiatorMSEHandshakeCommand.h b/src/InitiatorMSEHandshakeCommand.h new file mode 100644 index 00000000..a24e04e3 --- /dev/null +++ b/src/InitiatorMSEHandshakeCommand.h @@ -0,0 +1,80 @@ +/* */ +#ifndef _D_INITIATOR_MSE_HANDSHAKE_COMMAND_H_ +#define _D_INITIATOR_MSE_HANDSHAKE_COMMAND_H_ + +#include "PeerAbstractCommand.h" +#include "RequestGroupAware.h" +#include "BtContextAwareCommand.h" + +namespace aria2 { + +class MSEHandshake; + +class InitiatorMSEHandshakeCommand : public PeerAbstractCommand, + public BtContextAwareCommand, + public RequestGroupAware +{ +public: + enum Seq { + INITIATOR_SEND_KEY, + INITIATOR_WAIT_KEY, + INITIATOR_FIND_VC_MARKER, + INITIATOR_RECEIVE_PAD_D_LENGTH, + INITIATOR_RECEIVE_PAD_D, + }; +private: + Seq _sequence; + MSEHandshake* _mseHandshake; +protected: + virtual bool executeInternal(); + virtual bool prepareForNextPeer(int32_t wait); + virtual void onAbort(Exception* ex); + virtual bool exitBeforeExecute(); +public: + InitiatorMSEHandshakeCommand(int32_t cuid, + RequestGroup* requestGroup, + const SharedHandle& peer, + DownloadEngine* e, + const SharedHandle& btContext, + const SharedHandle& s); + + virtual ~InitiatorMSEHandshakeCommand(); + +}; + +} // namespace aria2 + +#endif // _D_INITIATOR_MSE_HANDSHAKE_COMMAND_H_ diff --git a/src/LibgcryptARC4Context.h b/src/LibgcryptARC4Context.h new file mode 100644 index 00000000..7dbfa4f1 --- /dev/null +++ b/src/LibgcryptARC4Context.h @@ -0,0 +1,96 @@ +/* */ +#ifndef _D_LIBGCRYPT_ARC4_CONTEXT_H_ +#define _D_LIBGCRYPT_ARC4_CONTEXT_H_ + +#include "common.h" +#include "DlAbortEx.h" +#include + +namespace aria2 { + +class LibgcryptARC4Context { +private: + gcry_cipher_hd_t _cipherCtx; + + void handleError(gcry_error_t errno) const + { + throw new DlAbortEx("Exception in libgcrypt routine(ARC4Context class): %s", + gcry_strerror(errno)); + } +public: + LibgcryptARC4Context():_cipherCtx(0) {} + + ~LibgcryptARC4Context() + { + gcry_cipher_close(_cipherCtx); + } + + gcry_cipher_hd_t getCipherContext() const + { + return _cipherCtx; + } + + void init(const unsigned char* key, size_t keyLength) + { + gcry_cipher_close(_cipherCtx); + + int algo = GCRY_CIPHER_ARCFOUR; + int mode = GCRY_CIPHER_MODE_STREAM; + unsigned int flags = 0; + { + gcry_error_t r = gcry_cipher_open(&_cipherCtx, algo, mode, flags); + if(r) { + handleError(r); + } + } + { + gcry_error_t r = gcry_cipher_setkey(_cipherCtx, key, keyLength); + if(r) { + handleError(r); + } + } + { + gcry_error_t r = gcry_cipher_setiv(_cipherCtx, 0, 0); + if(r) { + handleError(r); + } + } + } +}; + +} // namespace aria2 + +#endif // _D_LIBGCRYPT_ARC4_CONTEXT_H_ diff --git a/src/LibgcryptARC4Decryptor.h b/src/LibgcryptARC4Decryptor.h new file mode 100644 index 00000000..19e362a5 --- /dev/null +++ b/src/LibgcryptARC4Decryptor.h @@ -0,0 +1,77 @@ +/* */ +#ifndef _D_LIBGCRYPT_ARC4_DECRYPTOR_H_ +#define _D_LIBGCRYPT_ARC4_DECRYPTOR_H_ + +#include "common.h" +#include "DlAbortEx.h" +#include "LibgcryptARC4Context.h" +#include + +namespace aria2 { + +class ARC4Decryptor { +private: + LibgcryptARC4Context _ctx; + + void handleError(gcry_error_t errno) const + { + throw new DlAbortEx("Exception in libgcrypt routine(ARC4Decryptor class): %s", + gcry_strerror(errno)); + } +public: + ARC4Decryptor() {} + + ~ARC4Decryptor() {} + + void init(const unsigned char* key, size_t keyLength) + { + _ctx.init(key, keyLength); + } + + void decrypt(unsigned char* out, size_t outLength, + const unsigned char* in, size_t inLength) + { + gcry_error_t r = gcry_cipher_decrypt(_ctx.getCipherContext(), + out, outLength, in, inLength); + if(r) { + handleError(r); + } + } +}; + +} // namespace aria2 + +#endif // _D_LIBGCRYPT_ARC4_DECRYPTOR_H_ diff --git a/src/LibgcryptARC4Encryptor.h b/src/LibgcryptARC4Encryptor.h new file mode 100644 index 00000000..2e0c7a16 --- /dev/null +++ b/src/LibgcryptARC4Encryptor.h @@ -0,0 +1,77 @@ +/* */ +#ifndef _D_LIBGCRYPT_ARC4_ENCRYPTOR_H_ +#define _D_LIBGCRYPT_ARC4_ENCRYPTOR_H_ + +#include "common.h" +#include "DlAbortEx.h" +#include "LibgcryptARC4Context.h" +#include + +namespace aria2 { + +class ARC4Encryptor { +private: + LibgcryptARC4Context _ctx; + + void handleError(gcry_error_t errno) const + { + throw new DlAbortEx("Exception in libgcrypt routine(ARC4Encryptor class): %s", + gcry_strerror(errno)); + } +public: + ARC4Encryptor() {} + + ~ARC4Encryptor() {} + + void init(const unsigned char* key, size_t keyLength) + { + _ctx.init(key, keyLength); + } + + void encrypt(unsigned char* out, size_t outLength, + const unsigned char* in, size_t inLength) + { + gcry_error_t r = gcry_cipher_encrypt(_ctx.getCipherContext(), + out, outLength, in, inLength); + if(r) { + handleError(r); + } + } +}; + +} // namespace aria2 + +#endif // _D_LIBGCRYPT_ARC4_ENCRYPTOR_H_ diff --git a/src/LibgcryptDHKeyExchange.h b/src/LibgcryptDHKeyExchange.h new file mode 100644 index 00000000..f1eb9cb1 --- /dev/null +++ b/src/LibgcryptDHKeyExchange.h @@ -0,0 +1,164 @@ +/* */ +#ifndef _D_LIBGCRYPT_DH_KEY_EXCHANGE_H_ +#define _D_LIBGCRYPT_DH_KEY_EXCHANGE_H_ + +#include "common.h" +#include "DlAbortEx.h" +#include + +namespace aria2 { + +class DHKeyExchange { +private: + + gcry_mpi_t _prime; + + gcry_mpi_t _generator; + + gcry_mpi_t _privateKey; + + gcry_mpi_t _publicKey; + + void handleError(int errno) const + { + throw new DlAbortEx("Exception in libgcrypt routine(DHKeyExchange class): %s", + gcry_strerror(errno)); + } + +public: + DHKeyExchange():_prime(0), + _generator(0), + _privateKey(0), + _publicKey(0) {} + + ~DHKeyExchange() + { + gcry_mpi_release(_prime); + gcry_mpi_release(_generator); + gcry_mpi_release(_privateKey); + gcry_mpi_release(_publicKey); + } + + void init(const unsigned char* prime, const unsigned char* generator, + size_t privateKeyBits) + { + gcry_mpi_release(_prime); + gcry_mpi_release(_generator); + gcry_mpi_release(_privateKey); + { + gcry_error_t r = gcry_mpi_scan(&_prime, GCRYMPI_FMT_HEX, + prime, 0, 0); + if(r) { + handleError(r); + } + } + { + gcry_error_t r = gcry_mpi_scan(&_generator, GCRYMPI_FMT_HEX, + generator, 0, 0); + if(r) { + handleError(r); + } + } + _privateKey = gcry_mpi_new(0); + gcry_mpi_randomize(_privateKey, privateKeyBits, GCRY_STRONG_RANDOM); + } + + void generatePublicKey() + { + gcry_mpi_release(_publicKey); + _publicKey = gcry_mpi_new(0); + gcry_mpi_powm(_publicKey, _generator, _privateKey, _prime); + } + + size_t publicKeyLength() const + { + return (gcry_mpi_get_nbits(_publicKey)+7)/8; + } + + size_t getPublicKey(unsigned char* out, size_t outLength) const + { + if(outLength < publicKeyLength()) { + throw new DlAbortEx("Insufficient buffer for public key. expect:%u, actual:%u", + publicKeyLength(), outLength); + } + size_t nwritten; + gcry_error_t r = gcry_mpi_print(GCRYMPI_FMT_USG, out, outLength, &nwritten, _publicKey); + if(r) { + handleError(r); + } + return nwritten; + } + + void generateNonce(unsigned char* out, size_t outLength) const + { + gcry_create_nonce(out, outLength); + } + + size_t computeSecret(unsigned char* out, size_t outLength, + const unsigned char* peerPublicKeyData, + size_t peerPublicKeyLength) const + { + if(outLength < publicKeyLength()) { + throw new DlAbortEx("Insufficient buffer for secret. expect:%u, actual:%u", + publicKeyLength(), outLength); + } + gcry_mpi_t peerPublicKey; + { + gcry_error_t r = gcry_mpi_scan(&peerPublicKey, GCRYMPI_FMT_USG, peerPublicKeyData, + peerPublicKeyLength, 0); + if(r) { + handleError(r); + } + } + gcry_mpi_t secret = gcry_mpi_new(0); + gcry_mpi_powm(secret, peerPublicKey, _privateKey, _prime); + gcry_mpi_release(peerPublicKey); + + size_t nwritten; + { + gcry_error_t r = gcry_mpi_print(GCRYMPI_FMT_USG, out, outLength, &nwritten, secret); + gcry_mpi_release(secret); + if(r) { + handleError(r); + } + } + return nwritten; + } +}; + +} // namespace aria2 + +#endif // _D_LIBGCRYPT_DH_KEY_EXCHANGE_H_ diff --git a/src/LibsslARC4Context.h b/src/LibsslARC4Context.h new file mode 100644 index 00000000..27200c19 --- /dev/null +++ b/src/LibsslARC4Context.h @@ -0,0 +1,96 @@ +/* */ +#ifndef _D_LIBSSL_ARC4_CONTEXT_H_ +#define _D_LIBSSL_ARC4_CONTEXT_H_ + +#include "common.h" +#include "DlAbortEx.h" +#include +#include + +namespace aria2 { + +class LibsslARC4Context { +private: + EVP_CIPHER_CTX* _cipherCtx; + + void handleError() const + { + throw new DlAbortEx("Exception in libssl routine(ARC4Context class): %s", + ERR_error_string(ERR_get_error(), 0)); + } +public: + LibsslARC4Context():_cipherCtx(0) {} + + ~LibsslARC4Context() + { + if(_cipherCtx) { + EVP_CIPHER_CTX_cleanup(_cipherCtx); + } + delete _cipherCtx; + } + + EVP_CIPHER_CTX* getCipherContext() const + { + return _cipherCtx; + } + + // enc == 1: encryption + // enc == 0: decryption + void init(const unsigned char* key, size_t keyLength, int enc) + { + if(_cipherCtx) { + EVP_CIPHER_CTX_cleanup(_cipherCtx); + } + delete _cipherCtx; + _cipherCtx = new EVP_CIPHER_CTX; + EVP_CIPHER_CTX_init(_cipherCtx); + + if(!EVP_CipherInit_ex(_cipherCtx, EVP_rc4(), 0, 0, 0, enc)) { + handleError(); + } + if(!EVP_CIPHER_CTX_set_key_length(_cipherCtx, keyLength)) { + handleError(); + } + if(!EVP_CipherInit_ex(_cipherCtx, 0, 0, key, 0, -1)) { + handleError(); + } + } + +}; + +} // namespace aria2 + +#endif // _D_LIBSSL_ARC4_CONTEXT_H_ diff --git a/src/LibsslARC4Decryptor.h b/src/LibsslARC4Decryptor.h new file mode 100644 index 00000000..e51808c0 --- /dev/null +++ b/src/LibsslARC4Decryptor.h @@ -0,0 +1,77 @@ +/* */ +#ifndef _D_LIBSSL_ARC4_DECRYPTOR_H_ +#define _D_LIBSSL_ARC4_DECRYPTOR_H_ + +#include "common.h" +#include "DlAbortEx.h" +#include "LibsslARC4Context.h" +#include +#include + +namespace aria2 { + +class ARC4Decryptor { +private: + LibsslARC4Context _ctx; + + void handleError() const + { + throw new DlAbortEx("Exception in libssl routine(ARC4Decryptor class): %s", + ERR_error_string(ERR_get_error(), 0)); + } +public: + ARC4Decryptor() {} + + ~ARC4Decryptor() {} + + void init(const unsigned char* key, size_t keyLength) + { + _ctx.init(key, keyLength, 0); + } + + void decrypt(unsigned char* out, size_t outLength, + const unsigned char* in, size_t inLength) + { + int soutLength = outLength; + if(!EVP_CipherUpdate(_ctx.getCipherContext(), out, &soutLength, in, inLength)) { + handleError(); + } + } +}; + +} // namespace aria2 + +#endif // _D_LIBSSL_ARC4_DECRYPTOR_H_ diff --git a/src/LibsslARC4Encryptor.h b/src/LibsslARC4Encryptor.h new file mode 100644 index 00000000..49b8d5dc --- /dev/null +++ b/src/LibsslARC4Encryptor.h @@ -0,0 +1,77 @@ +/* */ +#ifndef _D_LIBSSL_ARC4_ENCRYPTOR_H_ +#define _D_LIBSSL_ARC4_ENCRYPTOR_H_ + +#include "common.h" +#include "DlAbortEx.h" +#include "LibsslARC4Context.h" +#include +#include + +namespace aria2 { + +class ARC4Encryptor { +private: + LibsslARC4Context _ctx; + + void handleError() const + { + throw new DlAbortEx("Exception in libssl routine(ARC4Encryptor class): %s", + ERR_error_string(ERR_get_error(), 0)); + } +public: + ARC4Encryptor() {} + + ~ARC4Encryptor() {} + + void init(const unsigned char* key, size_t keyLength) + { + _ctx.init(key, keyLength, 1); + } + + void encrypt(unsigned char* out, size_t outLength, + const unsigned char* in, size_t inLength) + { + int soutLength = outLength; + if(!EVP_CipherUpdate(_ctx.getCipherContext(), out, &soutLength, in, inLength)) { + handleError(); + } + } +}; + +} // namespace aria2 + +#endif // _D_LIBSSL_ARC4_ENCRYPTOR_H_ diff --git a/src/LibsslDHKeyExchange.h b/src/LibsslDHKeyExchange.h new file mode 100644 index 00000000..1f311d15 --- /dev/null +++ b/src/LibsslDHKeyExchange.h @@ -0,0 +1,171 @@ +/* */ +#ifndef _D_LIBSSL_DH_KEY_EXCHANGE_H_ +#define _D_LIBSSL_DH_KEY_EXCHANGE_H_ + +#include "common.h" +#include "DlAbortEx.h" +#include +#include +#include + +namespace aria2 { + +class DHKeyExchange { +private: + + BN_CTX* _bnCtx; + + BIGNUM* _prime; + + BIGNUM* _generator; + + BIGNUM* _privateKey; + + BIGNUM* _publicKey; + + void handleError() const + { + throw new DlAbortEx("Exception in libssl routine(DHKeyExchange class): %s", + ERR_error_string(ERR_get_error(), 0)); + } + +public: + DHKeyExchange():_bnCtx(0), + _prime(0), + _generator(0), + _privateKey(0), + _publicKey(0) {} + + ~DHKeyExchange() + { + BN_CTX_free(_bnCtx); + BN_free(_prime); + BN_free(_generator); + BN_free(_privateKey); + BN_free(_publicKey); + } + + void init(const unsigned char* prime, const unsigned char* generator, + size_t privateKeyBits) + { + BN_CTX_free(_bnCtx); + _bnCtx = BN_CTX_new(); + if(!_bnCtx) { + handleError(); + } + + BN_free(_prime); + _prime = 0; + BN_free(_generator); + _generator = 0; + BN_free(_privateKey); + _privateKey = 0; + + if(BN_hex2bn(&_prime, reinterpret_cast(prime)) == 0) { + handleError(); + } + if(BN_hex2bn(&_generator, reinterpret_cast(generator)) == 0) { + handleError(); + } + _privateKey = BN_new(); + if(!BN_rand(_privateKey, privateKeyBits, -1, false)) { + handleError(); + } + } + + void generatePublicKey() + { + BN_free(_publicKey); + _publicKey = BN_new(); + BN_mod_exp(_publicKey, _generator, _privateKey, _prime, _bnCtx); + } + + size_t publicKeyLength() const + { + return BN_num_bytes(_publicKey); + } + + size_t getPublicKey(unsigned char* out, size_t outLength) const + { + if(outLength < publicKeyLength()) { + throw new DlAbortEx("Insufficient buffer for public key. expect:%u, actual:%u", + publicKeyLength(), outLength); + } + size_t nwritten = BN_bn2bin(_publicKey, out); + if(!nwritten) { + handleError(); + } + return nwritten; + } + + void generateNonce(unsigned char* out, size_t outLength) const + { + if(!RAND_bytes(out, outLength)) { + handleError(); + } + } + + size_t computeSecret(unsigned char* out, size_t outLength, + const unsigned char* peerPublicKeyData, + size_t peerPublicKeyLength) const + { + if(outLength < publicKeyLength()) { + throw new DlAbortEx("Insufficient buffer for secret. expect:%u, actual:%u", + publicKeyLength(), outLength); + } + + + BIGNUM* peerPublicKey = BN_bin2bn(peerPublicKeyData, peerPublicKeyLength, 0); + if(!peerPublicKey) { + handleError(); + } + + BIGNUM* secret = BN_new(); + BN_mod_exp(secret, peerPublicKey, _privateKey, _prime, _bnCtx); + BN_free(peerPublicKey); + + size_t nwritten = BN_bn2bin(secret, out); + BN_free(secret); + if(!nwritten) { + handleError(); + } + return nwritten; + } +}; + +} // namespace aria2 + +#endif // _D_LIBSSL_DH_KEY_EXCHANGE_H_ diff --git a/src/MSEHandshake.cc b/src/MSEHandshake.cc new file mode 100644 index 00000000..63752643 --- /dev/null +++ b/src/MSEHandshake.cc @@ -0,0 +1,591 @@ +/* */ +#include "MSEHandshake.h" +#include "message.h" +#include "DlAbortEx.h" +#include "LogFactory.h" +#include "Logger.h" +#include "BtHandshakeMessage.h" +#include "Socket.h" +#include "a2netcompat.h" +#include "DHKeyExchange.h" +#include "ARC4Encryptor.h" +#include "ARC4Decryptor.h" +#include "MessageDigestHelper.h" +#include "SimpleRandomizer.h" +#include "Util.h" +#include "BtRegistry.h" +#include "BtContext.h" +#include +#include + +namespace aria2 { + +const unsigned char* MSEHandshake::PRIME = reinterpret_cast("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563"); + +const unsigned char* MSEHandshake::GENERATOR = reinterpret_cast("2"); + +const unsigned char MSEHandshake::VC[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +MSEHandshake::MSEHandshake(int32_t cuid, + const SocketHandle& socket, + const Option* op): + _cuid(cuid), + _socket(socket), + _option(op), + _logger(LogFactory::getInstance()), + _rbufLength(0), + _negotiatedCryptoType(CRYPTO_NONE), + _dh(0), + _encryptor(0), + _decryptor(0), + _initiator(true), + _markerIndex(0), + _padLength(0), + _iaLength(0), + _ia(0) +{} + +MSEHandshake::~MSEHandshake() +{ + delete _dh; + delete [] _ia; +} + +MSEHandshake::HANDSHAKE_TYPE MSEHandshake::identifyHandshakeType() +{ + if(!_socket->isReadable(0)) { + return HANDSHAKE_NOT_YET; + } + int32_t bufLength = 20; + _socket->peekData(_rbuf, bufLength); + if(bufLength != 20) { + return HANDSHAKE_NOT_YET; + } + if(_rbuf[0] == BtHandshakeMessage::PSTR_LENGTH && + memcmp(BtHandshakeMessage::BT_PSTR, _rbuf+1, 19) == 0) { + _logger->debug("CUID#%d - This is legacy BitTorrent handshake.", _cuid); + return HANDSHAKE_LEGACY; + } else { + _logger->debug("CUID#%d - This may be encrypted BitTorrent handshake.", _cuid); + return HANDSHAKE_ENCRYPTED; + } +} + +void MSEHandshake::initEncryptionFacility(bool initiator) +{ + delete _dh; + _dh = new DHKeyExchange(); + _dh->init(PRIME, GENERATOR, 160); + _dh->generatePublicKey(); + _logger->debug("CUID#%d - DH initialized.", _cuid); + + _initiator = initiator; +} + +void MSEHandshake::sendPublicKey() +{ + _logger->debug("CUID#%d - Sending public key.", _cuid); + unsigned char buffer[KEY_LENGTH+MAX_PAD_LENGTH]; + _dh->getPublicKey(buffer, KEY_LENGTH); + + size_t padLength = SimpleRandomizer::getInstance()->getRandomNumber(MAX_PAD_LENGTH+1); + _dh->generateNonce(buffer+KEY_LENGTH, padLength); + _socket->writeData(buffer, KEY_LENGTH+padLength); +} + +bool MSEHandshake::receivePublicKey() +{ + int32_t r = KEY_LENGTH-_rbufLength; + if(r > receiveNBytes(r)) { + return false; + } + _logger->debug("CUID#%d - public key received.", _cuid); + // TODO handle exception. in catch, resbufLength = 0; + _dh->computeSecret(_secret, sizeof(_secret), _rbuf, _rbufLength); + // reset _rbufLength + _rbufLength = 0; + return true; +} + +void MSEHandshake::initCipher(const unsigned char* infoHash) +{ + memcpy(_infoHash, infoHash, INFO_HASH_LENGTH); + //Initialize cipher + unsigned char s[4+KEY_LENGTH+INFO_HASH_LENGTH]; + memcpy(s, _initiator?"keyA":"keyB", 4); + memcpy(s+4, _secret, KEY_LENGTH); + memcpy(s+4+KEY_LENGTH, infoHash, INFO_HASH_LENGTH); + + unsigned char localCipherKey[20]; + MessageDigestHelper::digest(localCipherKey, sizeof(localCipherKey), "sha1", + s, sizeof(s)); + _encryptor = new ARC4Encryptor(); + _encryptor->init(localCipherKey, sizeof(localCipherKey)); + + unsigned char peerCipherKey[20]; + memcpy(s, _initiator?"keyB":"keyA", 4); + MessageDigestHelper::digest(peerCipherKey, sizeof(peerCipherKey), "sha1", + s, sizeof(s)); + _decryptor = new ARC4Decryptor(); + _decryptor->init(peerCipherKey, sizeof(peerCipherKey)); + + // discard first 1024 bytes ARC4 output. + unsigned char from[1024]; + unsigned char to[1024]; + _encryptor->encrypt(to, 1024, from, 1024); + _decryptor->decrypt(to, 1024, from, 1024); + + if(_initiator) { + ARC4Encryptor enc; + enc.init(peerCipherKey, sizeof(peerCipherKey)); + // discard first 1024 bytes ARC4 output. + enc.encrypt(to, 1024, from, 1024); + enc.encrypt(_initiatorVCMarker, sizeof(_initiatorVCMarker), VC, sizeof(VC)); + } +} + +ssize_t MSEHandshake::readDataAndDecrypt(unsigned char* data, size_t length) +{ + unsigned char temp[MAX_BUFFER_LENGTH]; + assert(MAX_BUFFER_LENGTH >= length); + int32_t rlength = length; + _socket->readData(temp, rlength); + _decryptor->decrypt(data, rlength, temp, rlength); + return rlength; +} + +void MSEHandshake::encryptAndSendData(const unsigned char* data, size_t length) +{ + unsigned char temp[4096]; + const unsigned char* dptr = data; + size_t s; + size_t r = length; + while(r > 0) { + s = r > sizeof(temp)?sizeof(temp):r; + _encryptor->encrypt(temp, s, dptr, s); + _socket->writeData(temp, s); + dptr += s; + r -= s; + } +} + +void MSEHandshake::createReq1Hash(unsigned char* md) const +{ + unsigned char buffer[100]; + memcpy(buffer, "req1", 4); + memcpy(buffer+4, _secret, KEY_LENGTH); + MessageDigestHelper::digest(md, 20, "sha1", buffer, 4+KEY_LENGTH); +} + +void MSEHandshake::createReq23Hash(unsigned char* md, const unsigned char* infoHash) const +{ + unsigned char x[24]; + memcpy(x, "req2", 4); + memcpy(x+4, infoHash, INFO_HASH_LENGTH); + unsigned char xh[20]; + MessageDigestHelper::digest(xh, sizeof(xh), "sha1", x, sizeof(x)); + + unsigned char y[4+96]; + memcpy(y, "req3", 4); + memcpy(y+4, _secret, KEY_LENGTH); + unsigned char yh[20]; + MessageDigestHelper::digest(yh, sizeof(yh), "sha1", y, sizeof(y)); + + for(size_t i = 0; i < 20; ++i) { + md[i] = xh[i]^yh[i]; + } +} + +uint16_t MSEHandshake::decodeLength16(const unsigned char* buffer) +{ + uint16_t be; + _decryptor->decrypt(reinterpret_cast(&be), + sizeof(be), + buffer, sizeof(be)); + return ntohs(be); +} + +void MSEHandshake::sendInitiatorStep2() +{ + _logger->debug("CUID#%d - Sending negotiation step2.", _cuid); + unsigned char md[20]; + createReq1Hash(md); + _socket->writeData(md, sizeof(md)); + + createReq23Hash(md, _infoHash); + _socket->writeData(md, sizeof(md)); + + { + unsigned char buffer[8+4+2+MAX_PAD_LENGTH+2]; + + // VC + memcpy(buffer, VC, sizeof(VC)); + // crypto_provide + unsigned char cryptoProvide[4]; + memset(cryptoProvide, 0, sizeof(cryptoProvide)); + cryptoProvide[3] = CRYPTO_PLAIN_TEXT | CRYPTO_ARC4; + memcpy(buffer+8, cryptoProvide, sizeof(cryptoProvide)); + + // len(padC) + uint16_t padCLength = SimpleRandomizer::getInstance()->getRandomNumber(MAX_PAD_LENGTH+1); + { + uint16_t padCLengthBE = htons(padCLength); + memcpy(buffer+8+4, &padCLengthBE, sizeof(padCLengthBE)); + } + // padC + memset(buffer+8+4+2, 0, padCLength); + // len(IA) + // currently, IA is zero-length. + uint16_t iaLength = 0; + { + uint16_t iaLengthBE = htons(iaLength); + memcpy(buffer+8+4+2+padCLength, &iaLengthBE, sizeof(iaLengthBE)); + } + encryptAndSendData(buffer, 8+4+2+padCLength+2); + } +} + +// This function reads exactly until the end of VC marker is reached. +bool MSEHandshake::findInitiatorVCMarker() +{ + // 616 is synchronization point of initiator + int32_t r = 616-KEY_LENGTH-_rbufLength; + if(!_socket->isReadable(0)) { + return false; + } + _socket->peekData(_rbuf+_rbufLength, r); + if(r == 0) { + throw new DlAbortEx(EX_EOF_FROM_PEER); + } + // find vc + { + std::string buf(&_rbuf[0], &_rbuf[_rbufLength+r]); + std::string vc(&_initiatorVCMarker[0], &_initiatorVCMarker[VC_LENGTH]); + if((_markerIndex = buf.find(vc)) == std::string::npos) { + if(616-KEY_LENGTH <= _rbufLength+r) { + throw new DlAbortEx("Failed to find VC marker."); + } else { + _socket->readData(_rbuf+_rbufLength, r); + _rbufLength += r; + return false; + } + } + } + assert(_markerIndex+VC_LENGTH-_rbufLength <= (size_t)r); + int32_t toRead = _markerIndex+VC_LENGTH-_rbufLength; + _socket->readData(_rbuf+_rbufLength, toRead); + _rbufLength += toRead; + + _logger->debug("CUID#%d - VC marker found at %u", _cuid, _markerIndex); + verifyVC(_rbuf+_markerIndex); + // reset _rbufLength + _rbufLength = 0; + return true; +} + +bool MSEHandshake::receiveInitiatorCryptoSelectAndPadDLength() +{ + int32_t r = CRYPTO_BITFIELD_LENGTH+2/* PadD length*/-_rbufLength; + if(r > receiveNBytes(r)) { + return false; + } + //verifyCryptoSelect + unsigned char* rbufptr = _rbuf; + { + unsigned char cryptoSelect[CRYPTO_BITFIELD_LENGTH]; + _decryptor->decrypt(cryptoSelect, sizeof(cryptoSelect), + rbufptr, sizeof(cryptoSelect)); + if(cryptoSelect[3]&CRYPTO_PLAIN_TEXT) { + _logger->debug("CUID#%d - peer prefers plaintext.", _cuid); + _negotiatedCryptoType = CRYPTO_PLAIN_TEXT; + } + if(cryptoSelect[3]&CRYPTO_ARC4) { + _logger->debug("CUID#%d - peer prefers ARC4", _cuid); + _negotiatedCryptoType = CRYPTO_ARC4; + } + if(_negotiatedCryptoType == CRYPTO_NONE) { + throw new DlAbortEx("CUID#%d - No supported crypto type selected.", _cuid); + } + } + // padD length + rbufptr += CRYPTO_BITFIELD_LENGTH; + _padLength = verifyPadLength(rbufptr, "PadD"); + // reset _rbufLength + _rbufLength = 0; + return true; +} + +bool MSEHandshake::receivePad() +{ + if(_padLength == 0) { + return true; + } + int32_t r = _padLength-_rbufLength; + if(r > receiveNBytes(r)) { + return false; + } + unsigned char temp[MAX_PAD_LENGTH]; + _decryptor->decrypt(temp, _padLength, _rbuf, _padLength); + // reset _rbufLength + _rbufLength = 0; + return true; +} + +bool MSEHandshake::findReceiverHashMarker() +{ + // 628 is synchronization limit of receiver. + int32_t r = 628-KEY_LENGTH-_rbufLength; + if(!_socket->isReadable(0)) { + return false; + } + _socket->peekData(_rbuf+_rbufLength, r); + if(r == 0) { + throw new DlAbortEx(EX_EOF_FROM_PEER); + } + // find hash('req1', S), S is _secret. + { + std::string buf(&_rbuf[0], &_rbuf[_rbufLength+r]); + unsigned char md[20]; + createReq1Hash(md); + std::string req1(&md[0], &md[sizeof(md)]); + if((_markerIndex = buf.find(req1)) == std::string::npos) { + if(628-KEY_LENGTH <= _rbufLength+r) { + throw new DlAbortEx("Failed to find hash marker."); + } else { + _socket->readData(_rbuf+_rbufLength, r); + _rbufLength += r; + return false; + } + } + } + assert(_markerIndex+20-_rbufLength <= (size_t)r); + int32_t toRead = _markerIndex+20-_rbufLength; + _socket->readData(_rbuf+_rbufLength, toRead); + _rbufLength += toRead; + + _logger->debug("CUID#%d - Hash marker found at %u.", _cuid, _markerIndex); + verifyReq1Hash(_rbuf+_markerIndex); + // reset _rbufLength + _rbufLength = 0; + return true; +} + +bool MSEHandshake::receiveReceiverHashAndPadCLength() +{ + int32_t r = 20+VC_LENGTH+CRYPTO_BITFIELD_LENGTH+2/*PadC length*/-_rbufLength; + if(r > receiveNBytes(r)) { + return false; + } + // resolve info hash + std::deque > btContexts = BtRegistry::getAllBtContext(); + // pointing to the position of HASH('req2', SKEY) xor HASH('req3', S) + unsigned char* rbufptr = _rbuf; + SharedHandle btContext = 0; + for(std::deque >::const_iterator i = btContexts.begin(); + i != btContexts.end(); ++i) { + unsigned char md[20]; + createReq23Hash(md, (*i)->getInfoHash()); + if(memcmp(md, rbufptr, sizeof(md)) == 0) { + _logger->debug("CUID#%d - info hash found: %s", _cuid, (*i)->getInfoHashAsString().c_str()); + btContext = *i; + break; + } + } + if(btContext.isNull()) { + throw new DlAbortEx("Unknown info hash."); + } + initCipher(btContext->getInfoHash()); + + // decrypt VC + rbufptr += 20; + verifyVC(rbufptr); + // decrypt crypto_provide + rbufptr += VC_LENGTH; + { + unsigned char cryptoProvide[4]; + _decryptor->decrypt(cryptoProvide, sizeof(cryptoProvide), + rbufptr, sizeof(cryptoProvide)); + // TODO choose the crypto type based on the preference. + // For now, choose ARC4. + if(cryptoProvide[3]&CRYPTO_PLAIN_TEXT) { + _logger->debug("CUID#%d - peer provides plaintext.", _cuid); + _negotiatedCryptoType = CRYPTO_PLAIN_TEXT; + } + if(cryptoProvide[3]&CRYPTO_ARC4) { + _logger->debug("CUID#%d - peer provides ARC4.", _cuid); + _negotiatedCryptoType = CRYPTO_ARC4; + } + if(_negotiatedCryptoType == CRYPTO_NONE) { + throw new DlAbortEx("CUID#%d - No supported crypto type provided.", _cuid); + } + } + // decrypt PadC length + rbufptr += CRYPTO_BITFIELD_LENGTH; + _padLength = verifyPadLength(rbufptr, "PadC"); + // reset _rbufLength + _rbufLength = 0; + return true; +} + +bool MSEHandshake::receiveReceiverIALength() +{ + int32_t r = 2-_rbufLength; + assert(r > 0); + if(r > receiveNBytes(r)) { + return false; + } + _iaLength = decodeLength16(_rbuf); + _logger->debug("CUID#%d - len(IA)=%u.", _cuid, _iaLength); + // reset _rbufLength + _rbufLength = 0; + return true; +} + +bool MSEHandshake::receiveReceiverIA() +{ + int32_t r = _iaLength-_rbufLength; + if(r > receiveNBytes(r)) { + return false; + } + delete [] _ia; + _ia = new unsigned char[_iaLength]; + _decryptor->decrypt(_ia, _iaLength, _rbuf, _iaLength); + _logger->debug("CUID#%d - IA received.", _cuid); + // reset _rbufLength + _rbufLength = 0; + return true; +} + +void MSEHandshake::sendReceiverStep2() +{ + unsigned char buffer[8+4+2+MAX_PAD_LENGTH]; + // VC + memcpy(buffer, VC, sizeof(VC)); + // crypto_select + unsigned char cryptoSelect[4]; + memset(cryptoSelect, 0, sizeof(cryptoSelect)); + cryptoSelect[3] = _negotiatedCryptoType; + memcpy(buffer+8, cryptoSelect, sizeof(cryptoSelect)); + // len(padD) + uint16_t padDLength = SimpleRandomizer::getInstance()->getRandomNumber(MAX_PAD_LENGTH+1); + { + uint16_t padDLengthBE = htons(padDLength); + memcpy(buffer+8+4, &padDLengthBE, sizeof(padDLengthBE)); + } + // padD, all zeroed + memset(buffer+8+4+2, 0, padDLength); + encryptAndSendData(buffer, 8+4+2+padDLength); +} + +uint16_t MSEHandshake::verifyPadLength(const unsigned char* padlenbuf, const std::string& padName) +{ + _logger->debug("CUID#%d - Veryfying Pad length for %s", _cuid, padName.c_str()); + + uint16_t padLength = decodeLength16(padlenbuf); + _logger->debug("CUID#%d - len(%s)=%u", _cuid, padName.c_str(), padLength); + if(padLength > 512) { + throw new DlAbortEx("Too large %s length: %u", padName.c_str(), padLength); + } + return padLength; +} + +void MSEHandshake::verifyVC(const unsigned char* vcbuf) +{ + _logger->debug("CUID#%d - Veryfying VC.", _cuid); + unsigned char vc[VC_LENGTH]; + _decryptor->decrypt(vc, sizeof(vc), vcbuf, sizeof(vc)); + if(memcmp(VC, vc, sizeof(VC)) != 0) { + throw new DlAbortEx("Invalid VC: %s", Util::toHex(vc, VC_LENGTH).c_str()); + } +} + +void MSEHandshake::verifyReq1Hash(const unsigned char* req1buf) +{ + _logger->debug("CUID#%d - Verifying req hash.", _cuid); + unsigned char md[20]; + createReq1Hash(md); + if(memcmp(md, req1buf, sizeof(md)) != 0) { + throw new DlAbortEx("Invalid req1 hash found."); + } +} + +ssize_t MSEHandshake::receiveNBytes(size_t bytes) +{ + int32_t r = bytes; + if(r > 0) { + if(!_socket->isReadable(0)) { + return 0; + } + _socket->readData(_rbuf+_rbufLength, r); + if(r == 0) { + throw new DlAbortEx(EX_EOF_FROM_PEER); + } + _rbufLength += r; + } + return r; +} + +const unsigned char* MSEHandshake::getIA() const +{ + return _ia; +} + +size_t MSEHandshake::getIALength() const +{ + return _iaLength; +} + +const unsigned char* MSEHandshake::getInfoHash() const +{ + return _infoHash; +} + +MSEHandshake::CRYPTO_TYPE MSEHandshake::getNegotiatedCryptoType() const +{ + return _negotiatedCryptoType; +} + +SharedHandle MSEHandshake::getEncryptor() const +{ + return _encryptor; +} + +SharedHandle MSEHandshake::getDecryptor() const +{ + return _decryptor; +} + +} // namespace aria2 diff --git a/src/MSEHandshake.h b/src/MSEHandshake.h new file mode 100644 index 00000000..4017186f --- /dev/null +++ b/src/MSEHandshake.h @@ -0,0 +1,179 @@ +/* */ +#ifndef _D_MSE_HANDSHAKE_H_ +#define _D_MSE_HANDSHAKE_H_ + +#include "common.h" +#include "SharedHandle.h" +#include "BtConstants.h" + +namespace aria2 { + +class Option; +class Logger; +class SocketCore; +class DHKeyExchange; +class ARC4Encryptor; +class ARC4Decryptor; + +class MSEHandshake { +public: + enum HANDSHAKE_TYPE { + HANDSHAKE_NOT_YET = 0, + HANDSHAKE_LEGACY, + HANDSHAKE_ENCRYPTED + }; + + enum CRYPTO_TYPE { + CRYPTO_NONE = 0, + CRYPTO_PLAIN_TEXT = 0x01, + CRYPTO_ARC4 = 0x02 + }; + +private: + static const size_t PRIME_BITS = 768; + + static const size_t KEY_LENGTH = (PRIME_BITS+7)/8; + + static const size_t MAX_PAD_LENGTH = 512; + + static const size_t VC_LENGTH = 8; + + static const size_t CRYPTO_BITFIELD_LENGTH = 4; + + static const size_t MAX_BUFFER_LENGTH = 6*1024; + + int32_t _cuid; + SharedHandle _socket; + const Option* _option; + const Logger* _logger; + + unsigned char _rbuf[MAX_BUFFER_LENGTH]; + size_t _rbufLength; + + CRYPTO_TYPE _negotiatedCryptoType; + DHKeyExchange* _dh; + SharedHandle _encryptor; + SharedHandle _decryptor; + unsigned char _infoHash[INFO_HASH_LENGTH]; + unsigned char _secret[KEY_LENGTH]; + bool _initiator; + unsigned char _initiatorVCMarker[VC_LENGTH]; + size_t _markerIndex; + uint16_t _padLength; + uint16_t _iaLength; + unsigned char* _ia; + + static const unsigned char* PRIME; + + static const unsigned char* GENERATOR; + + static const unsigned char VC[VC_LENGTH]; + + ssize_t readDataAndDecrypt(unsigned char* data, size_t length); + + void encryptAndSendData(const unsigned char* data, size_t length); + + void createReq1Hash(unsigned char* md) const; + + void createReq23Hash(unsigned char* md, const unsigned char* infoHash) const; + + uint16_t decodeLength16(const unsigned char* buffer); + + uint16_t decodeLength16(const char* buffer) + { + return decodeLength16(reinterpret_cast(buffer)); + } + + uint16_t verifyPadLength(const unsigned char* padlenbuf, + const std::string& padName); + + void verifyVC(const unsigned char* vcbuf); + + void verifyReq1Hash(const unsigned char* req1buf); + + ssize_t receiveNBytes(size_t bytes); + +public: + MSEHandshake(int32_t cuid, const SharedHandle& socket, + const Option* op); + + ~MSEHandshake(); + + HANDSHAKE_TYPE identifyHandshakeType(); + + void initEncryptionFacility(bool initiator); + + void sendPublicKey(); + + bool receivePublicKey(); + + void initCipher(const unsigned char* infoHash); + + void sendInitiatorStep2(); + + bool findInitiatorVCMarker(); + + bool receiveInitiatorCryptoSelectAndPadDLength(); + + bool receivePad(); + + bool findReceiverHashMarker(); + + bool receiveReceiverHashAndPadCLength(); + + bool receiveReceiverIALength(); + + bool receiveReceiverIA(); + + void sendReceiverStep2(); + + // returns plain text IA + const unsigned char* getIA() const; + + size_t getIALength() const; + + const unsigned char* getInfoHash() const; + + CRYPTO_TYPE getNegotiatedCryptoType() const; + + SharedHandle getEncryptor() const; + + SharedHandle getDecryptor() const; +}; + +} // namespace aria2 + +#endif // _D_MSE_HANDSHAKE_H_ diff --git a/src/Makefile.am b/src/Makefile.am index 3d6770e2..82e2b8a3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -310,7 +310,10 @@ SRCS += MetaEntry.h\ DHTRoutingTableSerializer.cc\ DHTRoutingTableDeserializer.cc\ DHTAutoSaveCommand.cc\ - DHTRegistry.cc + DHTRegistry.cc\ + InitiatorMSEHandshakeCommand.cc\ + ReceiverMSEHandshakeCommand.cc\ + MSEHandshake.cc endif # ENABLE_BITTORRENT if ENABLE_METALINK diff --git a/src/Makefile.in b/src/Makefile.in index 9aa9c76a..cfff1640 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -188,7 +188,10 @@ bin_PROGRAMS = aria2c$(EXEEXT) @ENABLE_BITTORRENT_TRUE@ DHTRoutingTableSerializer.cc\ @ENABLE_BITTORRENT_TRUE@ DHTRoutingTableDeserializer.cc\ @ENABLE_BITTORRENT_TRUE@ DHTAutoSaveCommand.cc\ -@ENABLE_BITTORRENT_TRUE@ DHTRegistry.cc +@ENABLE_BITTORRENT_TRUE@ DHTRegistry.cc\ +@ENABLE_BITTORRENT_TRUE@ InitiatorMSEHandshakeCommand.cc\ +@ENABLE_BITTORRENT_TRUE@ ReceiverMSEHandshakeCommand.cc\ +@ENABLE_BITTORRENT_TRUE@ MSEHandshake.cc @ENABLE_METALINK_TRUE@am__append_3 = Metalinker.cc Metalinker.h\ @ENABLE_METALINK_TRUE@ MetalinkEntry.cc MetalinkEntry.h\ @@ -440,17 +443,18 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \ DHTBucketRefreshCommand.cc DHTPeerAnnounceCommand.cc \ DHTReplaceNodeTask.cc DHTEntryPointNameResolveCommand.cc \ DHTRoutingTableSerializer.cc DHTRoutingTableDeserializer.cc \ - DHTAutoSaveCommand.cc DHTRegistry.cc Metalinker.cc \ - Metalinker.h MetalinkEntry.cc MetalinkEntry.h \ - MetalinkResource.cc MetalinkResource.h MetalinkProcessor.h \ - MetalinkProcessorFactory.cc MetalinkParserController.cc \ - MetalinkParserStateMachine.cc InitialMetalinkParserState.cc \ - MetalinkMetalinkParserState.cc FilesMetalinkParserState.cc \ - FileMetalinkParserState.cc SizeMetalinkParserState.cc \ - VersionMetalinkParserState.cc LanguageMetalinkParserState.cc \ - OSMetalinkParserState.cc VerificationMetalinkParserState.cc \ - HashMetalinkParserState.cc PiecesMetalinkParserState.cc \ - PieceHashMetalinkParserState.cc \ + DHTAutoSaveCommand.cc DHTRegistry.cc \ + InitiatorMSEHandshakeCommand.cc ReceiverMSEHandshakeCommand.cc \ + MSEHandshake.cc Metalinker.cc Metalinker.h MetalinkEntry.cc \ + MetalinkEntry.h MetalinkResource.cc MetalinkResource.h \ + MetalinkProcessor.h MetalinkProcessorFactory.cc \ + MetalinkParserController.cc MetalinkParserStateMachine.cc \ + InitialMetalinkParserState.cc MetalinkMetalinkParserState.cc \ + FilesMetalinkParserState.cc FileMetalinkParserState.cc \ + SizeMetalinkParserState.cc VersionMetalinkParserState.cc \ + LanguageMetalinkParserState.cc OSMetalinkParserState.cc \ + VerificationMetalinkParserState.cc HashMetalinkParserState.cc \ + PiecesMetalinkParserState.cc PieceHashMetalinkParserState.cc \ ResourcesMetalinkParserState.cc URLMetalinkParserState.cc \ FinMetalinkParserState.cc SkipTagMetalinkParserState.cc \ Metalink2RequestGroup.cc Metalink2RequestGroup.h \ @@ -571,7 +575,10 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \ @ENABLE_BITTORRENT_TRUE@ DHTRoutingTableSerializer.$(OBJEXT) \ @ENABLE_BITTORRENT_TRUE@ DHTRoutingTableDeserializer.$(OBJEXT) \ @ENABLE_BITTORRENT_TRUE@ DHTAutoSaveCommand.$(OBJEXT) \ -@ENABLE_BITTORRENT_TRUE@ DHTRegistry.$(OBJEXT) +@ENABLE_BITTORRENT_TRUE@ DHTRegistry.$(OBJEXT) \ +@ENABLE_BITTORRENT_TRUE@ InitiatorMSEHandshakeCommand.$(OBJEXT) \ +@ENABLE_BITTORRENT_TRUE@ ReceiverMSEHandshakeCommand.$(OBJEXT) \ +@ENABLE_BITTORRENT_TRUE@ MSEHandshake.$(OBJEXT) @ENABLE_METALINK_TRUE@am__objects_3 = Metalinker.$(OBJEXT) \ @ENABLE_METALINK_TRUE@ MetalinkEntry.$(OBJEXT) \ @ENABLE_METALINK_TRUE@ MetalinkResource.$(OBJEXT) \ @@ -1267,11 +1274,13 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpResponseCommand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InitialMetalinkParserState.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InitiateConnectionCommandFactory.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InitiatorMSEHandshakeCommand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IteratableChecksumValidator.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IteratableChunkChecksumValidator.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LanguageMetalinkParserState.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/List.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LogFactory.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MSEHandshake.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MemoryBufferPreDownloadHandler.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MessageDigestHelper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetaFileUtil.Po@am__quote@ @@ -1315,6 +1324,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PiecesMetalinkParserState.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Platform.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RealtimeCommand.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ReceiverMSEHandshakeCommand.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Request.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RequestGroup.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RequestGroupAware.Po@am__quote@ diff --git a/src/PeerAbstractCommand.cc b/src/PeerAbstractCommand.cc index 6ec06fd2..a3793843 100644 --- a/src/PeerAbstractCommand.cc +++ b/src/PeerAbstractCommand.cc @@ -106,10 +106,6 @@ bool PeerAbstractCommand::prepareForNextPeer(int32_t wait) { return true; } -bool PeerAbstractCommand::prepareForRetry(int32_t wait) { - return true; -} - void PeerAbstractCommand::disableReadCheckSocket() { if(checkSocketIsReadable) { e->deleteSocketForReadCheck(readCheckTarget, this); diff --git a/src/PeerAbstractCommand.h b/src/PeerAbstractCommand.h index e75fd954..4e532ada 100644 --- a/src/PeerAbstractCommand.h +++ b/src/PeerAbstractCommand.h @@ -57,7 +57,6 @@ protected: void setTimeout(int32_t timeout) { this->timeout = timeout; } virtual bool prepareForNextPeer(int32_t wait); - virtual bool prepareForRetry(int32_t wait); virtual void onAbort(Exception* ex) {}; virtual bool exitBeforeExecute() = 0; virtual bool executeInternal() = 0; diff --git a/src/PeerConnection.cc b/src/PeerConnection.cc index 83c6737d..e528ec1c 100644 --- a/src/PeerConnection.cc +++ b/src/PeerConnection.cc @@ -40,7 +40,11 @@ #include "BtHandshakeMessage.h" #include "Socket.h" #include "a2netcompat.h" +#include "ARC4Encryptor.h" +#include "ARC4Decryptor.h" #include +#include +#include namespace aria2 { @@ -53,7 +57,10 @@ PeerConnection::PeerConnection(int32_t cuid, logger(LogFactory::getInstance()), resbufLength(0), currentPayloadLength(0), - lenbufLength(0) + lenbufLength(0), + _encryptionEnabled(false), + _encryptor(0), + _decryptor(0) {} PeerConnection::~PeerConnection() {} @@ -62,7 +69,7 @@ int32_t PeerConnection::sendMessage(const unsigned char* data, int32_t dataLengt int32_t writtenLength = 0; if(socket->isWritable(0)) { // TODO fix this - socket->writeData((const char*)data, dataLength); + sendData(data, dataLength, _encryptionEnabled); writtenLength += dataLength; } return writtenLength; @@ -76,7 +83,7 @@ bool PeerConnection::receiveMessage(unsigned char* data, int32_t& dataLength) { // read payload size, 32bit unsigned integer int32_t remaining = 4-lenbufLength; int32_t temp = remaining; - socket->readData(lenbuf+lenbufLength, remaining); + readData(lenbuf+lenbufLength, remaining, _encryptionEnabled); if(remaining == 0) { // we got EOF logger->debug("CUID#%d - In PeerConnection::receiveMessage(), remain=%d", @@ -101,7 +108,7 @@ bool PeerConnection::receiveMessage(unsigned char* data, int32_t& dataLength) { int32_t remaining = currentPayloadLength-resbufLength; int32_t temp = remaining; if(remaining > 0) { - socket->readData(resbuf+resbufLength, remaining); + readData(resbuf+resbufLength, remaining, _encryptionEnabled); if(remaining == 0) { // we got EOF logger->debug("CUID#%d - In PeerConnection::receiveMessage(), payloadlen=%d, remaining=%d", @@ -132,7 +139,7 @@ bool PeerConnection::receiveHandshake(unsigned char* data, int32_t& dataLength, bool retval = true; if(remaining > 0) { int32_t temp = remaining; - socket->readData(resbuf+resbufLength, remaining); + readData(resbuf+resbufLength, remaining, _encryptionEnabled); if(remaining == 0) { // we got EOF logger->debug("CUID#%d - In PeerConnection::receiveHandshake(), remain=%d", @@ -153,4 +160,51 @@ bool PeerConnection::receiveHandshake(unsigned char* data, int32_t& dataLength, return retval; } +void PeerConnection::readData(char* data, int32_t& length, bool encryption) +{ + if(encryption) { + unsigned char* cdata = reinterpret_cast(data); + unsigned char temp[MAX_PAYLOAD_LEN]; + assert(MAX_PAYLOAD_LEN >= length); + socket->readData(temp, length); + _decryptor->decrypt(cdata, length, temp, length); + } else { + socket->readData(data, length); + } +} + +void PeerConnection::sendData(const unsigned char* data, size_t length, bool encryption) +{ + if(encryption) { + unsigned char temp[4096]; + const unsigned char* dptr = data; + size_t r = length; + while(r > 0) { + size_t s = std::min(r, sizeof(temp)); + _encryptor->encrypt(temp, s, dptr, s); + socket->writeData(temp, s); + dptr += s; + r -= s; + } + } else { + socket->writeData(data, length); + } +} + +void PeerConnection::enableEncryption(const SharedHandle& encryptor, + const SharedHandle& decryptor) +{ + _encryptor = encryptor; + _decryptor = decryptor; + + _encryptionEnabled = true; +} + +void PeerConnection::presetBuffer(const unsigned char* data, size_t length) +{ + size_t nwrite = std::min((size_t)MAX_PAYLOAD_LEN, length); + memcpy(resbuf, data, nwrite); + resbufLength = length; +} + } // namespace aria2 diff --git a/src/PeerConnection.h b/src/PeerConnection.h index 5f254600..5dad0fca 100644 --- a/src/PeerConnection.h +++ b/src/PeerConnection.h @@ -43,6 +43,8 @@ namespace aria2 { class Option; class Logger; class SocketCore; +class ARC4Encryptor; +class ARC4Decryptor; // we assume maximum length of incoming message is "piece" message with 16KB // data. Messages beyond that size are dropped. @@ -61,6 +63,14 @@ private: char lenbuf[4]; int32_t lenbufLength; + bool _encryptionEnabled; + SharedHandle _encryptor; + SharedHandle _decryptor; + + void readData(char* data, int32_t& length, bool encryption); + + void sendData(const unsigned char* data, size_t length, bool encryption); + public: PeerConnection(int32_t cuid, const SharedHandle& socket, const Option* op); @@ -78,6 +88,11 @@ public: * is assigned to 'length'. */ bool receiveHandshake(unsigned char* data, int32_t& dataLength, bool peek = false); + + void enableEncryption(const SharedHandle& encryptor, + const SharedHandle& decryptor); + + void presetBuffer(const unsigned char* data, size_t length); }; typedef SharedHandle PeerConnectionHandle; diff --git a/src/PeerInitiateConnectionCommand.cc b/src/PeerInitiateConnectionCommand.cc index 500e9acb..498af65e 100644 --- a/src/PeerInitiateConnectionCommand.cc +++ b/src/PeerInitiateConnectionCommand.cc @@ -33,8 +33,9 @@ */ /* copyright --> */ #include "PeerInitiateConnectionCommand.h" -#include "DownloadEngine.h" +#include "InitiatorMSEHandshakeCommand.h" #include "PeerInteractionCommand.h" +#include "DownloadEngine.h" #include "DlAbortEx.h" #include "message.h" #include "prefs.h" @@ -56,10 +57,12 @@ PeerInitiateConnectionCommand::PeerInitiateConnectionCommand(int cuid, RequestGroup* requestGroup, const PeerHandle& peer, DownloadEngine* e, - const BtContextHandle& btContext) + const BtContextHandle& btContext, + bool mseHandshakeEnabled) :PeerAbstractCommand(cuid, peer, e), BtContextAwareCommand(btContext), - RequestGroupAware(requestGroup) + RequestGroupAware(requestGroup), + _mseHandshakeEnabled(mseHandshakeEnabled) { btRuntime->increaseConnections(); } @@ -70,19 +73,19 @@ PeerInitiateConnectionCommand::~PeerInitiateConnectionCommand() } bool PeerInitiateConnectionCommand::executeInternal() { - Command* command; logger->info(MSG_CONNECTING_TO_SERVER, cuid, peer->ipaddr.c_str(), peer->port); socket->establishConnection(peer->ipaddr, peer->port); - command = - new PeerInteractionCommand(cuid, - _requestGroup, - peer, - e, - btContext, - socket, - PeerInteractionCommand::INITIATOR_SEND_HANDSHAKE); - + Command* command; + if(_mseHandshakeEnabled) { + command = + new InitiatorMSEHandshakeCommand(cuid, _requestGroup, peer, e, btContext, + socket); + } else { + command = + new PeerInteractionCommand(cuid, _requestGroup, peer, e, btContext, socket, + PeerInteractionCommand::INITIATOR_SEND_HANDSHAKE); + } e->commands.push_back(command); return true; } @@ -92,28 +95,14 @@ bool PeerInitiateConnectionCommand::prepareForNextPeer(int wait) { if(peerStorage->isPeerAvailable() && btRuntime->lessThanEqMinPeer()) { PeerHandle peer = peerStorage->getUnusedPeer(); peer->usedBy(CUIDCounterSingletonHolder::instance()->newID()); - PeerInitiateConnectionCommand* command = - new PeerInitiateConnectionCommand(peer->usedBy(), - _requestGroup, - peer, - e, + Command* command = + new PeerInitiateConnectionCommand(peer->usedBy(), _requestGroup, peer, e, btContext); e->commands.push_back(command); } return true; } -bool PeerInitiateConnectionCommand::prepareForRetry(int wait) { - PeerInitiateConnectionCommand* command = - new PeerInitiateConnectionCommand(cuid, - _requestGroup, - peer, - e, - btContext); - e->commands.push_back(command); - return true; -} - void PeerInitiateConnectionCommand::onAbort(Exception* ex) { peerStorage->returnPeer(peer); } diff --git a/src/PeerInitiateConnectionCommand.h b/src/PeerInitiateConnectionCommand.h index a3e22ecc..413d2273 100644 --- a/src/PeerInitiateConnectionCommand.h +++ b/src/PeerInitiateConnectionCommand.h @@ -45,9 +45,10 @@ class PeerInitiateConnectionCommand : public PeerAbstractCommand, public BtContextAwareCommand, public RequestGroupAware { +private: + bool _mseHandshakeEnabled; protected: virtual bool executeInternal(); - virtual bool prepareForRetry(int wait); virtual bool prepareForNextPeer(int wait); virtual void onAbort(Exception* ex); virtual bool exitBeforeExecute(); @@ -57,7 +58,8 @@ public: RequestGroup* requestGroup, const SharedHandle& peer, DownloadEngine* e, - const SharedHandle& btContext); + const SharedHandle& btContext, + bool mseHandshakeEnabled = true); virtual ~PeerInitiateConnectionCommand(); }; diff --git a/src/PeerInteractionCommand.cc b/src/PeerInteractionCommand.cc index 1c241e39..34af2271 100644 --- a/src/PeerInteractionCommand.cc +++ b/src/PeerInteractionCommand.cc @@ -259,11 +259,6 @@ bool PeerInteractionCommand::prepareForNextPeer(int32_t wait) { return true; } -bool PeerInteractionCommand::prepareForRetry(int32_t wait) { - e->commands.push_back(this); - return false; -} - void PeerInteractionCommand::onAbort(Exception* ex) { btInteractive->cancelAllPiece(); peerStorage->returnPeer(peer); diff --git a/src/PeerInteractionCommand.h b/src/PeerInteractionCommand.h index e27974c0..15bb4d42 100644 --- a/src/PeerInteractionCommand.h +++ b/src/PeerInteractionCommand.h @@ -60,7 +60,6 @@ private: int32_t maxDownloadSpeedLimit; protected: virtual bool executeInternal(); - virtual bool prepareForRetry(int32_t wait); virtual bool prepareForNextPeer(int32_t wait); virtual void onAbort(Exception* ex); virtual bool exitBeforeExecute(); diff --git a/src/PeerListenCommand.cc b/src/PeerListenCommand.cc index 40ddcfa9..f7fd5e7c 100644 --- a/src/PeerListenCommand.cc +++ b/src/PeerListenCommand.cc @@ -39,7 +39,7 @@ #include "RecoverableException.h" #include "CUIDCounter.h" #include "message.h" -#include "PeerReceiveHandshakeCommand.h" +#include "ReceiverMSEHandshakeCommand.h" #include "Logger.h" #include "Socket.h" #include @@ -99,17 +99,19 @@ bool PeerListenCommand::execute() { if(peerInfo.first == localInfo.first) { continue; } + // Since peerSocket may be in non-blocking mode, make it blocking mode + // here. + peerSocket->setBlockingMode(); + PeerHandle peer = new Peer(peerInfo.first, 0); - PeerReceiveHandshakeCommand* command = - new PeerReceiveHandshakeCommand(CUIDCounterSingletonHolder::instance()->newID(), - peer, e, peerSocket); + int32_t cuid = CUIDCounterSingletonHolder::instance()->newID(); + Command* command = + new ReceiverMSEHandshakeCommand(cuid, peer, e, peerSocket); e->commands.push_back(command); logger->debug("Accepted the connection from %s:%u.", peer->ipaddr.c_str(), peer->port); - logger->debug("Added CUID#%d to receive Bt handshake.", - command->getCuid()); - + logger->debug("Added CUID#%d to receive BitTorrent/MSE handshake.", cuid); } catch(RecoverableException* ex) { logger->debug(MSG_ACCEPT_FAILURE, ex, cuid); delete ex; diff --git a/src/PeerReceiveHandshakeCommand.cc b/src/PeerReceiveHandshakeCommand.cc index 7d55fa9f..34b261de 100644 --- a/src/PeerReceiveHandshakeCommand.cc +++ b/src/PeerReceiveHandshakeCommand.cc @@ -55,11 +55,16 @@ namespace aria2 { PeerReceiveHandshakeCommand::PeerReceiveHandshakeCommand(int32_t cuid, const PeerHandle& peer, DownloadEngine* e, - const SocketHandle& s): + const SocketHandle& s, + const SharedHandle& peerConnection): PeerAbstractCommand(cuid, peer, e, s), - _peerConnection(new PeerConnection(cuid, s, e->option)), + _peerConnection(peerConnection), _lowestSpeedLimit(20*1024) -{} +{ + if(_peerConnection.isNull()) { + _peerConnection = new PeerConnection(cuid, socket, e->option); + } +} PeerReceiveHandshakeCommand::~PeerReceiveHandshakeCommand() {} diff --git a/src/PeerReceiveHandshakeCommand.h b/src/PeerReceiveHandshakeCommand.h index fed9049c..2ab0ab80 100644 --- a/src/PeerReceiveHandshakeCommand.h +++ b/src/PeerReceiveHandshakeCommand.h @@ -61,7 +61,8 @@ public: PeerReceiveHandshakeCommand(int32_t cuid, const SharedHandle& peer, DownloadEngine* e, - const SharedHandle& s); + const SharedHandle& s, + const SharedHandle& peerConnection = 0); virtual ~PeerReceiveHandshakeCommand(); diff --git a/src/ReceiverMSEHandshakeCommand.cc b/src/ReceiverMSEHandshakeCommand.cc new file mode 100644 index 00000000..89cbcd10 --- /dev/null +++ b/src/ReceiverMSEHandshakeCommand.cc @@ -0,0 +1,157 @@ +/* */ +#include "ReceiverMSEHandshakeCommand.h" +#include "PeerReceiveHandshakeCommand.h" +#include "PeerConnection.h" +#include "DownloadEngine.h" +#include "BtHandshakeMessage.h" +#include "DlAbortEx.h" +#include "Peer.h" +#include "message.h" +#include "Socket.h" +#include "Logger.h" +#include "prefs.h" +#include "Option.h" +#include "MSEHandshake.h" +#include "ARC4Encryptor.h" +#include "ARC4Decryptor.h" + +namespace aria2 { + +ReceiverMSEHandshakeCommand::ReceiverMSEHandshakeCommand +(int32_t cuid, + const SharedHandle& peer, + DownloadEngine* e, + const SharedHandle& s): + + PeerAbstractCommand(cuid, peer, e, s), + _sequence(RECEIVER_IDENTIFY_HANDSHAKE), + _mseHandshake(new MSEHandshake(cuid, s, e->option)) +{ + setTimeout(e->option->getAsInt(PREF_PEER_CONNECTION_TIMEOUT)); +} + +ReceiverMSEHandshakeCommand::~ReceiverMSEHandshakeCommand() +{ + delete _mseHandshake; +} + +bool ReceiverMSEHandshakeCommand::exitBeforeExecute() +{ + return e->isHaltRequested(); +} + +bool ReceiverMSEHandshakeCommand::executeInternal() +{ + switch(_sequence) { + case RECEIVER_IDENTIFY_HANDSHAKE: { + MSEHandshake::HANDSHAKE_TYPE type = _mseHandshake->identifyHandshakeType(); + switch(type) { + case MSEHandshake::HANDSHAKE_NOT_YET: + break; + case MSEHandshake::HANDSHAKE_ENCRYPTED: + _mseHandshake->initEncryptionFacility(false); + _sequence = RECEIVER_WAIT_KEY; + break; + case MSEHandshake::HANDSHAKE_LEGACY: { + Command* c = new PeerReceiveHandshakeCommand(cuid, peer, e, socket); + e->commands.push_back(c); + return true; + } + default: + throw new DlAbortEx("Not supported handshake type."); + } + break; + } + case RECEIVER_WAIT_KEY: { + if(_mseHandshake->receivePublicKey()) { + _mseHandshake->sendPublicKey(); + _sequence = RECEIVER_FIND_HASH_MARKER; + } + break; + } + case RECEIVER_FIND_HASH_MARKER: { + if(_mseHandshake->findReceiverHashMarker()) { + _sequence = RECEIVER_RECEIVE_PAD_C_LENGTH; + } + break; + } + case RECEIVER_RECEIVE_PAD_C_LENGTH: { + if(_mseHandshake->receiveReceiverHashAndPadCLength()) { + _sequence = RECEIVER_RECEIVE_PAD_C; + } + break; + } + case RECEIVER_RECEIVE_PAD_C: { + if(_mseHandshake->receivePad()) { + _sequence = RECEIVER_RECEIVE_IA_LENGTH; + } + break; + } + case RECEIVER_RECEIVE_IA_LENGTH: { + if(_mseHandshake->receiveReceiverIALength()) { + _sequence = RECEIVER_RECEIVE_IA; + } + break; + } + case RECEIVER_RECEIVE_IA: { + if(_mseHandshake->receiveReceiverIA()) { + _mseHandshake->sendReceiverStep2(); + SharedHandle peerConnection = + new PeerConnection(cuid, socket, e->option); + if(_mseHandshake->getNegotiatedCryptoType() == MSEHandshake::CRYPTO_ARC4) { + peerConnection->enableEncryption(_mseHandshake->getEncryptor(), + _mseHandshake->getDecryptor()); + } + if(_mseHandshake->getIALength() > 0) { + peerConnection->presetBuffer(_mseHandshake->getIA(), + _mseHandshake->getIALength()); + } + // TODO add _mseHandshake->getInfoHash() to PeerReceiveHandshakeCommand + // as a hint. If this info hash and one in BitTorrent Handshake does not + // match, then drop connection. + Command* c = + new PeerReceiveHandshakeCommand(cuid, peer, e, socket, peerConnection); + e->commands.push_back(c); + return true; + } + break; + } + } + e->commands.push_back(this); + return false; +} + +} // namespace aria2 diff --git a/src/ReceiverMSEHandshakeCommand.h b/src/ReceiverMSEHandshakeCommand.h new file mode 100644 index 00000000..1bfae418 --- /dev/null +++ b/src/ReceiverMSEHandshakeCommand.h @@ -0,0 +1,76 @@ +/* */ +#ifndef _D_RECEIVER_MSE_HANDSHAKE_COMMAND_H_ +#define _D_RECEIVER_MSE_HANDSHAKE_COMMAND_H_ + +#include "PeerAbstractCommand.h" + +namespace aria2 { + +class MSEHandshake; +class SocketCore; +class Peer; + +class ReceiverMSEHandshakeCommand:public PeerAbstractCommand +{ +public: + enum Seq { + RECEIVER_IDENTIFY_HANDSHAKE, + RECEIVER_WAIT_KEY, + RECEIVER_FIND_HASH_MARKER, + RECEIVER_RECEIVE_PAD_C_LENGTH, + RECEIVER_RECEIVE_PAD_C, + RECEIVER_RECEIVE_IA_LENGTH, + RECEIVER_RECEIVE_IA + }; +private: + Seq _sequence; + + MSEHandshake* _mseHandshake; +protected: + virtual bool executeInternal(); + virtual bool exitBeforeExecute(); +public: + ReceiverMSEHandshakeCommand(int32_t cuid, + const SharedHandle& peer, + DownloadEngine* e, + const SharedHandle& s); + + virtual ~ReceiverMSEHandshakeCommand(); +}; + +} // namespace aria2 + +#endif // _D_RECEIVER_MSE_HANDSHAKE_COMMAND_H_ diff --git a/src/SocketCore.h b/src/SocketCore.h index 992b5bb3..77f71428 100644 --- a/src/SocketCore.h +++ b/src/SocketCore.h @@ -180,6 +180,10 @@ public: { writeData(msg.c_str(), msg.size()); } + void writeData(const unsigned char* data, int32_t len) + { + writeData(reinterpret_cast(data), len); + } void writeData(const char* data, size_t len, const std::string& host, uint16_t port); @@ -197,6 +201,11 @@ public: */ void readData(char* data, int32_t& len); + void readData(unsigned char* data, int32_t& len) + { + readData(reinterpret_cast(data), len); + } + ssize_t readDataFrom(char* data, size_t len, struct sockaddr* sender, socklen_t* senderLength); @@ -217,6 +226,11 @@ public: */ void peekData(char* data, int32_t& len); + void peekData(unsigned char* data, int32_t& len) + { + peekData(reinterpret_cast(data), len); + } + /** * Makes this socket secure. * If the system has not OpenSSL, then this method do nothing. diff --git a/test/ARC4Test.cc b/test/ARC4Test.cc new file mode 100644 index 00000000..1bab1163 --- /dev/null +++ b/test/ARC4Test.cc @@ -0,0 +1,51 @@ +#include "ARC4Encryptor.h" +#include "ARC4Decryptor.h" +#include "Exception.h" +#include "Util.h" +#include "DHTUtil.h" +#include +#include + +namespace aria2 { + +class ARC4Test:public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE(ARC4Test); + CPPUNIT_TEST(testEncryptDecrypt); + CPPUNIT_TEST_SUITE_END(); +public: + void setUp() {} + + void tearDown() {} + + void testEncryptDecrypt(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(ARC4Test); + +void ARC4Test::testEncryptDecrypt() +{ + ARC4Encryptor enc; + ARC4Decryptor dec; + const size_t LEN = 20; + unsigned char key[LEN]; + memset(key, 0, LEN); + DHTUtil::generateRandomData(key, sizeof(key)); + enc.init(key, sizeof(key)); + dec.init(key, sizeof(key)); + + unsigned char encrypted[LEN]; + unsigned char decrypted[LEN]; + enc.encrypt(encrypted, LEN, key, LEN); + dec.decrypt(decrypted, LEN, encrypted, LEN); + + CPPUNIT_ASSERT(memcmp(key, decrypted, LEN) == 0); + // once more + enc.encrypt(encrypted, LEN, key, LEN); + dec.decrypt(decrypted, LEN, encrypted, LEN); + + CPPUNIT_ASSERT(memcmp(key, decrypted, LEN) == 0); +} + +} // namespace aria2 diff --git a/test/DHKeyExchangeTest.cc b/test/DHKeyExchangeTest.cc new file mode 100644 index 00000000..02479cd6 --- /dev/null +++ b/test/DHKeyExchangeTest.cc @@ -0,0 +1,52 @@ +#include "DHKeyExchange.h" +#include "Exception.h" +#include "Util.h" +#include + +namespace aria2 { + +class DHKeyExchangeTest:public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE(DHKeyExchangeTest); + CPPUNIT_TEST(testHandshake); + CPPUNIT_TEST_SUITE_END(); +public: + void setUp() {} + + void tearDown() {} + + void testHandshake(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(DHKeyExchangeTest); + +void DHKeyExchangeTest::testHandshake() +{ + DHKeyExchange dhA; + DHKeyExchange dhB; + + const unsigned char* PRIME = reinterpret_cast("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563"); + + const unsigned char* GENERATOR = reinterpret_cast("2"); + + dhA.init(PRIME, GENERATOR, 160); + dhB.init(PRIME, GENERATOR, 160); + + dhA.generatePublicKey(); + dhB.generatePublicKey(); + + unsigned char publicKeyA[96]; + unsigned char publicKeyB[96]; + dhA.getPublicKey(publicKeyA, sizeof(publicKeyA)); + dhB.getPublicKey(publicKeyB, sizeof(publicKeyB)); + + unsigned char secretA[96]; + unsigned char secretB[96]; + dhA.computeSecret(secretA, sizeof(secretA), publicKeyB, sizeof(publicKeyB)); + dhB.computeSecret(secretB, sizeof(secretB), publicKeyA, sizeof(publicKeyA)); + + CPPUNIT_ASSERT(memcmp(secretA, secretB, sizeof(secretA)) == 0); +} + +} // namespace aria2 diff --git a/test/DefaultBtAnnounceTest.cc b/test/DefaultBtAnnounceTest.cc index 2b1548d7..bcaa37ef 100644 --- a/test/DefaultBtAnnounceTest.cc +++ b/test/DefaultBtAnnounceTest.cc @@ -113,35 +113,35 @@ void DefaultBtAnnounceTest::testNoMoreAnnounce() btAnnounce.setRandomizer(new FixedNumberRandomizer()); btAnnounce.generateKey(); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceFailure(); - CPPUNIT_ASSERT_EQUAL(std::string("http://backup/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://backup/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); _pieceStorage->setAllDownloadFinished(true); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=completed"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=completed&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); - CPPUNIT_ASSERT_EQUAL(std::string("http://backup/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=completed"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://backup/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=completed&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); _btRuntime->setHalt(true); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=0&no_peer_id=1&port=6989&event=stopped"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=0&no_peer_id=1&port=6989&event=stopped&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); - CPPUNIT_ASSERT_EQUAL(std::string("http://backup/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=0&no_peer_id=1&port=6989&event=stopped"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://backup/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=0&no_peer_id=1&port=6989&event=stopped&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); @@ -164,23 +164,23 @@ void DefaultBtAnnounceTest::testGetAnnounceUrl() btAnnounce.setRandomizer(new FixedNumberRandomizer()); btAnnounce.generateKey(); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); _pieceStorage->setAllDownloadFinished(true); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=completed"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=completed&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); _btRuntime->setHalt(true); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=0&no_peer_id=1&port=6989&event=stopped"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=0&no_peer_id=1&port=6989&event=stopped&supportcrypto=1"), btAnnounce.getAnnounceUrl()); } void DefaultBtAnnounceTest::testIsAllAnnounceFailed() @@ -206,11 +206,11 @@ void DefaultBtAnnounceTest::testIsAllAnnounceFailed() btAnnounce.setRandomizer(new FixedNumberRandomizer()); btAnnounce.generateKey(); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceFailure(); - CPPUNIT_ASSERT_EQUAL(std::string("http://backup/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://backup/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceFailure(); @@ -238,17 +238,17 @@ void DefaultBtAnnounceTest::testURLOrderInStoppedEvent() btAnnounce.setRandomizer(new FixedNumberRandomizer()); btAnnounce.generateKey(); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost1/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost1/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); _btRuntime->setHalt(true); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost1/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=0&no_peer_id=1&port=6989&event=stopped"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost1/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=0&no_peer_id=1&port=6989&event=stopped&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceFailure(); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost2/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=0&no_peer_id=1&port=6989&event=stopped"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost2/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=0&no_peer_id=1&port=6989&event=stopped&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); } @@ -268,17 +268,17 @@ void DefaultBtAnnounceTest::testURLOrderInCompletedEvent() btAnnounce.setRandomizer(new FixedNumberRandomizer()); btAnnounce.generateKey(); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost1/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost1/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=started&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); _pieceStorage->setAllDownloadFinished(true); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost1/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=completed"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost1/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=completed&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceFailure(); - CPPUNIT_ASSERT_EQUAL(std::string("http://localhost2/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=completed"), btAnnounce.getAnnounceUrl()); + CPPUNIT_ASSERT_EQUAL(std::string("http://localhost2/announce?info_hash=%01%23Eg%89%ab%cd%ef%01%23Eg%89%ab%cd%ef%01%23Eg&peer_id=%2daria2%2dultrafastdltl&uploaded=1572864&downloaded=1310720&left=1572864&compact=1&key=AAAAAAAA&numwant=50&no_peer_id=1&port=6989&event=completed&supportcrypto=1"), btAnnounce.getAnnounceUrl()); btAnnounce.announceSuccess(); } diff --git a/test/Makefile.am b/test/Makefile.am index dda77038..64430e12 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -128,7 +128,9 @@ aria2c_SOURCES += BtAllowedFastMessageTest.cc\ XORCloserTest.cc\ DHTIDCloserTest.cc\ DHTRoutingTableSerializerTest.cc\ - DHTRoutingTableDeserializerTest.cc + DHTRoutingTableDeserializerTest.cc\ + DHKeyExchangeTest.cc\ + ARC4Test.cc endif # ENABLE_BITTORRENT if ENABLE_METALINK diff --git a/test/Makefile.in b/test/Makefile.in index f5d4e185..66d2845d 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -113,7 +113,9 @@ check_PROGRAMS = $(am__EXEEXT_1) @ENABLE_BITTORRENT_TRUE@ XORCloserTest.cc\ @ENABLE_BITTORRENT_TRUE@ DHTIDCloserTest.cc\ @ENABLE_BITTORRENT_TRUE@ DHTRoutingTableSerializerTest.cc\ -@ENABLE_BITTORRENT_TRUE@ DHTRoutingTableDeserializerTest.cc +@ENABLE_BITTORRENT_TRUE@ DHTRoutingTableDeserializerTest.cc\ +@ENABLE_BITTORRENT_TRUE@ DHKeyExchangeTest.cc\ +@ENABLE_BITTORRENT_TRUE@ ARC4Test.cc @ENABLE_METALINK_TRUE@am__append_3 = MetalinkerTest.cc\ @ENABLE_METALINK_TRUE@ MetalinkEntryTest.cc\ @@ -207,8 +209,9 @@ am__aria2c_SOURCES_DIST = AllTest.cc SocketCoreTest.cc \ DHTPeerAnnounceEntryTest.cc DHTPeerAnnounceStorageTest.cc \ DHTTokenTrackerTest.cc XORCloserTest.cc DHTIDCloserTest.cc \ DHTRoutingTableSerializerTest.cc \ - DHTRoutingTableDeserializerTest.cc MetalinkerTest.cc \ - MetalinkEntryTest.cc Metalink2RequestGroupTest.cc \ + DHTRoutingTableDeserializerTest.cc DHKeyExchangeTest.cc \ + ARC4Test.cc MetalinkerTest.cc MetalinkEntryTest.cc \ + Metalink2RequestGroupTest.cc \ MetalinkPostDownloadHandlerTest.cc MetalinkHelperTest.cc \ MetalinkParserControllerTest.cc MetalinkProcessorTest.cc @ENABLE_MESSAGE_DIGEST_TRUE@am__objects_1 = \ @@ -284,7 +287,9 @@ am__aria2c_SOURCES_DIST = AllTest.cc SocketCoreTest.cc \ @ENABLE_BITTORRENT_TRUE@ XORCloserTest.$(OBJEXT) \ @ENABLE_BITTORRENT_TRUE@ DHTIDCloserTest.$(OBJEXT) \ @ENABLE_BITTORRENT_TRUE@ DHTRoutingTableSerializerTest.$(OBJEXT) \ -@ENABLE_BITTORRENT_TRUE@ DHTRoutingTableDeserializerTest.$(OBJEXT) +@ENABLE_BITTORRENT_TRUE@ DHTRoutingTableDeserializerTest.$(OBJEXT) \ +@ENABLE_BITTORRENT_TRUE@ DHKeyExchangeTest.$(OBJEXT) \ +@ENABLE_BITTORRENT_TRUE@ ARC4Test.$(OBJEXT) @ENABLE_METALINK_TRUE@am__objects_3 = MetalinkerTest.$(OBJEXT) \ @ENABLE_METALINK_TRUE@ MetalinkEntryTest.$(OBJEXT) \ @ENABLE_METALINK_TRUE@ Metalink2RequestGroupTest.$(OBJEXT) \ @@ -619,6 +624,7 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ARC4Test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AllTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AlphaNumberDecoratorTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AnnounceListTest.Po@am__quote@ @@ -653,6 +659,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieBoxFactoryTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieBoxTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieParserTest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DHKeyExchangeTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DHTAnnouncePeerMessageTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DHTAnnouncePeerReplyMessageTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DHTBucketTest.Po@am__quote@