diff --git a/ChangeLog b/ChangeLog index b85a41772..bf911b971 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * feature to encrypt peer connections with a secret AES-256 key stored in .torrent file * deprecated compact storage allocation * close files in separate thread on systems where close() may block (Mac OS X for instance) * don't create all directories up front when adding torrents diff --git a/examples/make_torrent.cpp b/examples/make_torrent.cpp index ff1bda9fa..2c5cb3c3e 100644 --- a/examples/make_torrent.cpp +++ b/examples/make_torrent.cpp @@ -85,8 +85,10 @@ void print_usage() " If this is not specified, the torrent file is\n" " printed to the standard out, except on windows\n" " where the filename defaults to a.torrent\n" - "-c add root certificate to the torrent, to make\n" - " it an SSL torrent\n" + "-c file add root certificate to the torrent, to verify\n" + " the HTTPS tracker\n" + "-e file add an AES-256 encryption key. This is used\n" + " to encrypt every peer connection\n" , stderr); } @@ -112,6 +114,7 @@ int main(int argc, char* argv[]) int piece_size = 0; int flags = 0; std::string root_cert; + std::string encryption_key; std::string outfile; std::string merklefile; @@ -167,6 +170,10 @@ int main(int argc, char* argv[]) ++i; root_cert = argv[i]; break; + case 'e': + ++i; + encryption_key = argv[i]; + break; default: print_usage(); return 1; @@ -207,15 +214,29 @@ int main(int argc, char* argv[]) if (!root_cert.empty()) { - FILE* cert = fopen(root_cert.c_str(), "rb"); - if (cert) + std::vector pem; + load_file(root_cert, pem, ec, 10000); + if (ec) { - std::string pem; - pem.resize(5000); - int s = fread(&pem[0], 1, pem.size(), cert); - pem.resize(s); - t.set_root_cert(pem); - fclose(cert); + fprintf(stderr, "failed to load root certificate for tracker: %s\n", ec.message().c_str()); + } + else + { + t.set_root_cert(std::string(&pem[0], pem.size())); + } + } + + if (!encryption_key.empty()) + { + std::vector key; + load_file(encryption_key, key, ec, 32); + if (ec) + { + fprintf(stderr, "failed to load AES-256 encryption key: %s\n", ec.message().c_str()); + } + else + { + t.set_encryption_key(std::string(&key[0], key.size())); } } diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 3f8aaabea..37f5b74af 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -613,6 +613,12 @@ namespace libtorrent torrent_map m_torrents; std::map > m_uuids; + // these are all the torrents using full AES-256 encryption of + // all peer connections. When receiving a handshake that's encrypred + // these are the torrents we need to try to decrypt it with to + // find the decryption key + std::set > m_encrypted_torrents; + typedef std::list > check_queue_t; // this has all torrents that wants to be checked in it diff --git a/include/libtorrent/bt_peer_connection.hpp b/include/libtorrent/bt_peer_connection.hpp index f33804949..c35b4240f 100644 --- a/include/libtorrent/bt_peer_connection.hpp +++ b/include/libtorrent/bt_peer_connection.hpp @@ -298,7 +298,7 @@ public: void append_send_buffer(char* buffer, int size, Destructor const& destructor) { #ifndef TORRENT_DISABLE_ENCRYPTION - if (m_enc_handler) + if (m_rc4_encrypted) m_enc_handler->encrypt(buffer, size); #endif peer_connection::append_send_buffer(buffer, size, destructor, true); diff --git a/include/libtorrent/create_torrent.hpp b/include/libtorrent/create_torrent.hpp index a7412665c..73d0ce917 100644 --- a/include/libtorrent/create_torrent.hpp +++ b/include/libtorrent/create_torrent.hpp @@ -90,6 +90,7 @@ namespace libtorrent void add_node(std::pair const& node); void add_tracker(std::string const& url, int tier = 0); void set_root_cert(std::string const& pem); + void set_encryption_key(std::string const& key); void set_priv(bool p) { m_private = p; } int num_pieces() const { return m_files.num_pieces(); } @@ -149,6 +150,11 @@ namespace libtorrent // this is the root cert for SSL torrents std::string m_root_cert; + // if this is an encrypted torrent, this is the + // symmetric encryption key every stream is + // encrypted by + std::string m_encryption_key; + // this is used when creating a torrent. If there's // only one file there are cases where it's impossible // to know if it should be written as a multifile torrent diff --git a/include/libtorrent/pe_crypto.hpp b/include/libtorrent/pe_crypto.hpp index 5ec6fbe0d..65814965e 100644 --- a/include/libtorrent/pe_crypto.hpp +++ b/include/libtorrent/pe_crypto.hpp @@ -41,6 +41,8 @@ POSSIBILITY OF SUCH DAMAGE. #include #elif defined TORRENT_USE_OPENSSL #include +#include +#include #else // RC4 state from libtomcrypt struct rc4 { @@ -197,6 +199,104 @@ namespace libtorrent bool m_decrypt; }; +#ifdef TORRENT_USE_OPENSSL + struct aes256_handler : encryption_handler + { + aes256_handler() : m_enc_pos(0), m_dec_pos(0) + { + EVP_CIPHER_CTX_init(&m_enc); + EVP_CIPHER_CTX_init(&m_dec); + } + + ~aes256_handler() + { + EVP_CIPHER_CTX_cleanup(&m_enc); + EVP_CIPHER_CTX_cleanup(&m_dec); + } + + void set_incoming_key(unsigned char const* in_key, int len) + { + const int nrounds = 5; + boost::uint8_t salt[8] = { 0xf1, 0x03, 0x46, 0xe2, 0xb1, 0xa8, 0x29, 0x63 }; + boost::uint8_t key[32]; + boost::uint8_t iv[32]; + + int i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, in_key, len, nrounds, key, iv); + TORRENT_ASSERT(len == 32); + EVP_EncryptInit_ex(&m_enc, EVP_aes_256_cbc(), NULL, key, iv); + // since we're using the AES as a stream cipher, both the encrypt and + // decrypt context will in fact only _encrypt_ stuff, so initializing + // this as encrypt is not a typo + EVP_EncryptInit_ex(&m_dec, EVP_aes_256_cbc(), NULL, key, iv); + m_enc_pos = 0; + m_dec_pos = 0; + std::memcpy(m_enc_state, iv, sizeof(m_enc_state)); + std::memcpy(m_dec_state, iv, sizeof(m_enc_state)); + } + void set_outgoing_key(unsigned char const* key, int len) { /* no-op */ } + void encrypt(char* pos, int len) + { + while (len > 0) + { + while (m_enc_pos < AES_BLOCK_SIZE && len > 0) + { + *pos ^= m_enc_state[m_enc_pos]; + ++m_enc_pos; + ++pos; + --len; + } + + if (m_enc_pos == AES_BLOCK_SIZE) + { + next_block(&m_enc, m_enc_state); + m_enc_pos = 0; + } + } + } + + void decrypt(char* pos, int len) + { + while (len > 0) + { + while (m_dec_pos < AES_BLOCK_SIZE && len > 0) + { + *pos ^= m_dec_state[m_dec_pos]; + ++m_dec_pos; + ++pos; + --len; + } + + if (m_dec_pos == AES_BLOCK_SIZE) + { + next_block(&m_dec, m_dec_state); + m_dec_pos = 0; + } + } + } + + private: + + // we're turning the AES block-cipher into a stream cipher. This + // function will produce the next block in the sequence of + // block-sized buffers. We're using "Output feedback" (OFB) mode. + void next_block(EVP_CIPHER_CTX* ctx, boost::uint8_t* pad) + { + int outlen = AES_BLOCK_SIZE; + EVP_EncryptUpdate(ctx, pad, &outlen, pad, AES_BLOCK_SIZE); + TORRENT_ASSERT(outlen == AES_BLOCK_SIZE); + } + + EVP_CIPHER_CTX m_enc; + EVP_CIPHER_CTX m_dec; + + boost::uint8_t m_enc_state[AES_BLOCK_SIZE]; + boost::uint8_t m_dec_state[AES_BLOCK_SIZE]; + int m_enc_pos; + int m_dec_pos; + + }; +#endif // TORRENT_USE_OPENSSL + } // namespace libtorrent #endif // TORRENT_PE_CRYPTO_HPP_INCLUDED diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 749089637..ee4201e47 100644 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -664,7 +664,11 @@ namespace libtorrent void reset_recv_buffer(int packet_size); void set_soft_packet_size(int size) { m_soft_packet_size = size; } - void attach_to_torrent(sha1_hash const& ih); + // if allow_encrypted is false, and the torrent 'ih' turns out + // to be an encrypted torrent (AES-256 encrypted) the peer will + // be disconnected. This is to prevent non-encrypted peers to + // attach to an encrypted torrent + void attach_to_torrent(sha1_hash const& ih, bool allow_encrypted); bool verify_piece(peer_request const& p) const; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index f4f947b1d..5ec785aff 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -1324,6 +1324,12 @@ namespace libtorrent // data into this torrent instead of replacing them bool m_merge_resume_trackers:1; + // set to true if this torrent has been added to the session + // global list for encrypted torrents. When the torrent is + // paused it's removed and when it's started again, it's + // re-added + bool m_in_encrypted_list:1; + #if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS public: // set to false until we've loaded resume data diff --git a/include/libtorrent/torrent_info.hpp b/include/libtorrent/torrent_info.hpp index 4dc222f0e..ddac7f924 100644 --- a/include/libtorrent/torrent_info.hpp +++ b/include/libtorrent/torrent_info.hpp @@ -333,8 +333,12 @@ namespace libtorrent // ------- end deprecation ------- #endif +#ifdef TORRENT_USE_OPENSSL std::string const& ssl_cert() const { return m_ssl_root_cert; } + std::string const& encryption_key() const { return m_aes_key; } +#endif + bool is_valid() const { return m_files.is_valid(); } bool priv() const { return m_private; } @@ -450,6 +454,8 @@ namespace libtorrent // pointing to the first byte of the first sha-1 hash char const* m_piece_hashes; + // TODO: these strings could be lazy_entry* to save memory + // if a comment is found in the torrent file // this will be set to that comment std::string m_comment; @@ -458,11 +464,16 @@ namespace libtorrent // to create the torrent file std::string m_created_by; +#ifdef TORRENT_USE_OPENSSL // for ssl-torrens, this contains the root // certificate, in .pem format (i.e. ascii // base64 encoded with head and tails) std::string m_ssl_root_cert; + // used to encrypt the peer connections + std::string m_aes_key; +#endif + // the info section parsed. points into m_info_section // parsed lazily mutable lazy_entry m_info_dict; diff --git a/src/bt_peer_connection.cpp b/src/bt_peer_connection.cpp index df3332a88..c22bf39d0 100644 --- a/src/bt_peer_connection.cpp +++ b/src/bt_peer_connection.cpp @@ -37,6 +37,10 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#ifdef TORRENT_USE_OPENSSL +#include // autp_ptr +#endif + #include "libtorrent/bt_peer_connection.hpp" #include "libtorrent/session.hpp" #include "libtorrent/identify_client.hpp" @@ -127,6 +131,21 @@ namespace libtorrent m_in_constructor = false; #endif memset(m_reserved_bits, 0, sizeof(m_reserved_bits)); + +#ifdef TORRENT_USE_OPENSSL + boost::shared_ptr t = tor.lock(); + std::string const key = t->torrent_file().encryption_key(); + if (key.size() == 32) + { + m_enc_handler.reset(new aes256_handler); + m_enc_handler->set_incoming_key((const unsigned char*)key.c_str(), key.size()); + m_encrypted = true; + m_rc4_encrypted = true; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** encrypted torrent. enabling AES-256 encryption"); +#endif + } +#endif } bt_peer_connection::bt_peer_connection( @@ -198,7 +217,19 @@ namespace libtorrent { #ifndef TORRENT_DISABLE_ENCRYPTION - pe_settings::enc_policy const& out_enc_policy = m_ses.get_pe_settings().out_enc_policy; + pe_settings::enc_policy out_enc_policy = m_ses.get_pe_settings().out_enc_policy; + +#ifdef TORRENT_USE_OPENSSL + // if this torrent is using AES-256 encryption, don't + // also enable the normal encryption + boost::shared_ptr t = associated_torrent().lock(); + std::string const key = t->torrent_file().encryption_key(); + if (key.size() == 32) out_enc_policy = pe_settings::disabled; +#endif +#ifdef TORRENT_VERBOSE_LOGGING + char const* policy_name[] = {"forced", "enabled", "disabled"}; + peer_log("*** outgoing encryption policy: %s", policy_name[out_enc_policy]); +#endif if (out_enc_policy == pe_settings::forced) { @@ -2532,6 +2563,13 @@ namespace libtorrent for (i = m_ses.m_torrents.begin(); i != m_ses.m_torrents.end(); ++i) { torrent const& ti = *i->second; + +#ifdef TORRENT_USE_OPENSSL + // don't consider encrypted torrents (since that would + // open up a hole to connecting to them without the key) + if (ti.torrent_file().encryption_key().size() == 32) continue; +#endif + sha1_hash const& skey_hash = ti.obfuscated_hash(); sha1_hash obfs_hash = m_dh_key_exchange->get_hash_xor_mask(); obfs_hash ^= skey_hash; @@ -2541,7 +2579,7 @@ namespace libtorrent { if (!t) { - attach_to_torrent(ti.info_hash()); + attach_to_torrent(ti.info_hash(), false); if (is_disconnecting()) return; t = associated_torrent().lock(); @@ -2889,52 +2927,102 @@ namespace libtorrent recv_buffer = receive_buffer(); int packet_size = recv_buffer[0]; - const char protocol_string[] = "BitTorrent protocol"; + const char protocol_string[] = "\x13" "BitTorrent protocol"; if (packet_size != 19 || - !std::equal(recv_buffer.begin + 1, recv_buffer.begin + 19, protocol_string)) + memcmp(recv_buffer.begin, protocol_string, 20) != 0) { #ifndef TORRENT_DISABLE_ENCRYPTION - if (!is_local() && m_ses.get_pe_settings().in_enc_policy == pe_settings::disabled) - { - disconnect(errors::no_incoming_encrypted); - return; - } +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** unrecognized protocol header"); +#endif - // Don't attempt to perform an encrypted handshake - // within an encrypted connection - if (!m_encrypted && !is_local()) + bool found_encrypted_torrent = false; +#ifdef TORRENT_USE_OPENSSL + if (!is_local()) { + std::auto_ptr handler(new aes256_handler); + boost::uint8_t temp_pad[20]; + + for (std::set >::iterator i = m_ses.m_encrypted_torrents.begin() + , end(m_ses.m_encrypted_torrents.end()); i != end; ++i) + { + boost::shared_ptr t = *i; + std::string const key = t->torrent_file().encryption_key(); + TORRENT_ASSERT(key.size() == 32); + handler->set_incoming_key((const unsigned char*)key.c_str(), key.size()); + std::memcpy(temp_pad, recv_buffer.begin, 20); + handler->decrypt((char*)temp_pad, 20); + if (memcmp(temp_pad, protocol_string, 20) != 0) continue; + + // we found the key that could decrypt it + m_rc4_encrypted = true; + m_encrypted = true; + m_enc_handler.reset(handler.release()); + found_encrypted_torrent = true; +#ifdef TORRENT_VERBOSE_LOGGING + peer_log("*** found encrypted torrent"); +#endif + TORRENT_ASSERT(recv_buffer.left() == 20); +// handler->decrypt((char*)recv_buffer.begin + 20, recv_buffer.left() - 20); + break; + } + } +#endif + + if (!found_encrypted_torrent) + { + + if (!is_local() + && m_ses.get_pe_settings().in_enc_policy == pe_settings::disabled) + { + disconnect(errors::no_incoming_encrypted); + return; + } + + // Don't attempt to perform an encrypted handshake + // within an encrypted connection. For local connections, + // we're expected to already have passed the encrypted + // handshake by this point + if (m_encrypted || is_local()) + { + disconnect(errors::invalid_info_hash, 1); + return; + } + #ifdef TORRENT_VERBOSE_LOGGING peer_log("*** attempting encrypted connection"); #endif m_state = read_pe_dhkey; cut_receive_buffer(0, dh_key_len); TORRENT_ASSERT(!packet_finished()); - return; + return; } TORRENT_ASSERT((!is_local() && m_encrypted) || is_local()); -#endif // #ifndef TORRENT_DISABLE_ENCRYPTION +#else disconnect(errors::invalid_info_hash, 1); return; +#endif // TORRENT_DISABLE_ENCRYPTION } - -#ifndef TORRENT_DISABLE_ENCRYPTION - TORRENT_ASSERT(m_state != read_pe_dhkey); - - if (!is_local() && - (m_ses.get_pe_settings().in_enc_policy == pe_settings::forced) && - !m_encrypted) + else { - disconnect(errors::no_incoming_regular); - return; - } +#ifndef TORRENT_DISABLE_ENCRYPTION + TORRENT_ASSERT(m_state != read_pe_dhkey); + + if (!is_local() && + (m_ses.get_pe_settings().in_enc_policy == pe_settings::forced) && + !m_encrypted) + { + disconnect(errors::no_incoming_regular); + return; + } #endif #ifdef TORRENT_VERBOSE_LOGGING - peer_log("<== BitTorrent protocol"); + peer_log("<== BitTorrent protocol"); #endif + } m_state = read_info_hash; reset_recv_buffer(28); @@ -2990,7 +3078,7 @@ namespace libtorrent std::copy(recv_buffer.begin + 8, recv_buffer.begin + 28 , (char*)info_hash.begin()); - attach_to_torrent(info_hash); + attach_to_torrent(info_hash, m_encrypted && m_rc4_encrypted); if (is_disconnecting()) return; } else diff --git a/src/create_torrent.cpp b/src/create_torrent.cpp index 43c53be96..fe115fad7 100644 --- a/src/create_torrent.cpp +++ b/src/create_torrent.cpp @@ -301,6 +301,9 @@ namespace libtorrent if (!m_root_cert.empty()) info["ssl-cert"] = m_root_cert; + if (!m_encryption_key.empty()) + info["encryption-key"] = m_encryption_key; + if (m_private) info["private"] = 1; if (!m_multifile) diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index b825d3b58..1eee27831 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -1115,7 +1115,7 @@ namespace libtorrent && t->to_req(piece_block(p.piece, p.start / t->block_size())) == p; } - void peer_connection::attach_to_torrent(sha1_hash const& ih) + void peer_connection::attach_to_torrent(sha1_hash const& ih, bool allow_encrypted) { INVARIANT_CHECK; @@ -1148,6 +1148,14 @@ namespace libtorrent return; } +#ifdef TORRENT_USE_OPENSSL + if (t->torrent_file().encryption_key().size() == 32 && !allow_encrypted) + { + disconnect(errors::invalid_info_hash, 2); + return; + } +#endif + if (t->is_paused() && (!t->is_auto_managed() || !m_ses.m_settings.incoming_starts_queued_torrents)) { diff --git a/src/torrent.cpp b/src/torrent.cpp index d28dd85e2..fd7901e13 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -412,6 +412,7 @@ namespace libtorrent , m_magnet_link(false) , m_apply_ip_filter(p.apply_ip_filter) , m_merge_resume_trackers(p.merge_resume_trackers) + , m_in_encrypted_list(false) { #if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS m_resume_data_loaded = false; @@ -1367,6 +1368,14 @@ namespace libtorrent } #endif +#ifdef TORRENT_USE_OPENSSL + if (m_torrent_file->encryption_key().size() == 32 && !m_in_encrypted_list) + { + m_ses.m_encrypted_torrents.insert(shared_from_this()); + m_in_encrypted_list = true; + } +#endif + m_file_priority.resize(m_torrent_file->num_files(), 1); m_file_progress.resize(m_torrent_file->num_files(), 0); @@ -3192,6 +3201,14 @@ namespace libtorrent if (m_abort) return; +#ifdef TORRENT_USE_OPENSSL + if (m_torrent_file->is_valid() && m_torrent_file->encryption_key().size() == 32 && m_in_encrypted_list) + { + m_ses.m_encrypted_torrents.erase(shared_from_this()); + m_in_encrypted_list = false; + } +#endif + m_abort = true; // if the torrent is paused, it doesn't need // to announce with even=stopped again. @@ -6410,6 +6427,14 @@ namespace libtorrent TORRENT_ASSERT(m_ses.is_network_thread()); if (!is_paused()) return; +#ifdef TORRENT_USE_OPENSSL + if (m_torrent_file->is_valid() && m_torrent_file->encryption_key().size() == 32 && m_in_encrypted_list) + { + m_ses.m_encrypted_torrents.erase(shared_from_this()); + m_in_encrypted_list = false; + } +#endif + #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) @@ -6560,6 +6585,14 @@ namespace libtorrent TORRENT_ASSERT(m_ses.is_network_thread()); if (is_paused()) return; +#ifdef TORRENT_USE_OPENSSL + if (m_torrent_file->is_valid() && m_torrent_file->encryption_key().size() == 32 && !m_in_encrypted_list) + { + m_ses.m_encrypted_torrents.insert(shared_from_this()); + m_in_encrypted_list = true; + } +#endif + #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) diff --git a/src/torrent_info.cpp b/src/torrent_info.cpp index 2e69bfa22..6005a230c 100644 --- a/src/torrent_info.cpp +++ b/src/torrent_info.cpp @@ -467,6 +467,10 @@ namespace libtorrent , m_piece_hashes(t.m_piece_hashes) , m_comment(t.m_comment) , m_created_by(t.m_created_by) +#ifdef TORRENT_USE_OPENSSL + , m_ssl_root_cert(t.m_ssl_root_cert) + , m_aes_key(t.m_aes_key) +#endif , m_creation_date(t.m_creation_date) , m_info_hash(t.m_info_hash) , m_merkle_first_leaf(t.m_merkle_first_leaf) @@ -933,8 +937,12 @@ namespace libtorrent m_private = info.dict_find_int_value("private", 0); +#ifdef TORRENT_USE_OPENSSL m_ssl_root_cert = info.dict_find_string_value("ssl-cert"); + m_aes_key = info.dict_find_string_value("encryption-key"); +#endif + return true; } diff --git a/test/setup_transfer.cpp b/test/setup_transfer.cpp index db56a09e1..ae6cc9c46 100644 --- a/test/setup_transfer.cpp +++ b/test/setup_transfer.cpp @@ -203,7 +203,7 @@ boost::intrusive_ptr clone_ptr(boost::intrusive_ptr const& ptr) } boost::intrusive_ptr create_torrent(std::ostream* file, int piece_size - , int num_pieces, bool add_tracker) + , int num_pieces, bool add_tracker, bool encrypted_torrent) { char const* tracker_url = "http://non-existent-name.com/announce"; // excercise the path when encountering invalid urls @@ -243,7 +243,17 @@ boost::intrusive_ptr create_torrent(std::ostream* file, int piece_ std::vector tmp; std::back_insert_iterator > out(tmp); - bencode(out, t.generate()); + entry tor = t.generate(); + + if (encrypted_torrent) + { + std::string key; + key.resize(32); + std::generate(key.begin(), key.end(), &std::rand); + tor["info"]["encryption-key"] = key; + } + + bencode(out, tor); error_code ec; return boost::intrusive_ptr(new torrent_info( &tmp[0], tmp.size(), ec)); @@ -254,7 +264,7 @@ setup_transfer(session* ses1, session* ses2, session* ses3 , bool clear_files, bool use_metadata_transfer, bool connect_peers , std::string suffix, int piece_size , boost::intrusive_ptr* torrent, bool super_seeding - , add_torrent_params const* p, bool stop_lsd) + , add_torrent_params const* p, bool stop_lsd, bool encrypted_torrent) { assert(ses1); assert(ses2); @@ -298,7 +308,7 @@ setup_transfer(session* ses1, session* ses2, session* ses3 error_code ec; create_directory("./tmp1" + suffix, ec); std::ofstream file(("./tmp1" + suffix + "/temporary").c_str()); - t = ::create_torrent(&file, piece_size, 19, true); + t = ::create_torrent(&file, piece_size, 19, true, encrypted_torrent); file.close(); if (clear_files) { diff --git a/test/setup_transfer.hpp b/test/setup_transfer.hpp index eb990fb7a..4a59c5eec 100644 --- a/test/setup_transfer.hpp +++ b/test/setup_transfer.hpp @@ -53,7 +53,7 @@ void wait_for_listen(libtorrent::session& ses, char const* name); void test_sleep(int millisec); boost::intrusive_ptr create_torrent(std::ostream* file = 0 - , int piece_size = 16 * 1024, int num_pieces = 13, bool add_tracker = true); + , int piece_size = 16 * 1024, int num_pieces = 13, bool add_tracker = true, bool encrypted = false); boost::tuple* torrent = 0, bool super_seeding = false - , libtorrent::add_torrent_params const* p = 0, bool stop_lsd = true); + , libtorrent::add_torrent_params const* p = 0, bool stop_lsd = true, bool encrypted_torrent = false); int start_web_server(bool ssl = false, bool chunked = false); void stop_web_server(); diff --git a/test/test_pe_crypto.cpp b/test/test_pe_crypto.cpp index a80ffc075..0b4ee3b74 100644 --- a/test/test_pe_crypto.cpp +++ b/test/test_pe_crypto.cpp @@ -74,7 +74,7 @@ void display_pe_settings(libtorrent::pe_settings s) void test_transfer(libtorrent::pe_settings::enc_policy policy, libtorrent::pe_settings::enc_level level = libtorrent::pe_settings::both, - bool pref_rc4 = false) + bool pref_rc4 = false, bool encrypted_torrent = false) { using namespace libtorrent; using std::cerr; @@ -106,7 +106,8 @@ void test_transfer(libtorrent::pe_settings::enc_policy policy, torrent_handle tor2; using boost::tuples::ignore; - boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0, true, false, true, "_pe"); + boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0, true, false, true + , "_pe", 16 * 1024, 0, false, 0, true, encrypted_torrent); std::cerr << "waiting for transfer to complete\n"; @@ -131,6 +132,32 @@ void test_transfer(libtorrent::pe_settings::enc_policy policy, remove_all("./tmp3_pe", ec); } +void test_enc_handler(libtorrent::encryption_handler* a, libtorrent::encryption_handler* b) +{ + int repcount = 128; + for (int rep = 0; rep < repcount; ++rep) + { + std::size_t buf_len = rand() % (512 * 1024); + char* buf = new char[buf_len]; + char* cmp_buf = new char[buf_len]; + + std::generate(buf, buf + buf_len, &std::rand); + std::memcpy(cmp_buf, buf, buf_len); + + a->encrypt(buf, buf_len); + TEST_CHECK(!std::equal(buf, buf + buf_len, cmp_buf)); + b->decrypt(buf, buf_len); + TEST_CHECK(std::equal(buf, buf + buf_len, cmp_buf)); + + b->encrypt(buf, buf_len); + TEST_CHECK(!std::equal(buf, buf + buf_len, cmp_buf)); + a->decrypt(buf, buf_len); + TEST_CHECK(std::equal(buf, buf + buf_len, cmp_buf)); + + delete[] buf; + delete[] cmp_buf; + } +} int test_main() { @@ -156,35 +183,31 @@ int test_main() sha1_hash test1_key = hasher("test1_key",8).final(); sha1_hash test2_key = hasher("test2_key",8).final(); + fprintf(stderr, "testing RC4 handler\n"); rc4_handler rc41; rc41.set_incoming_key(&test2_key[0], 20); rc41.set_outgoing_key(&test1_key[0], 20); rc4_handler rc42; rc42.set_incoming_key(&test1_key[0], 20); rc42.set_outgoing_key(&test2_key[0], 20); - - for (int rep = 0; rep < repcount; ++rep) - { - std::size_t buf_len = rand() % (512 * 1024); - char* buf = new char[buf_len]; - char* zero_buf = new char[buf_len]; - - std::fill(buf, buf + buf_len, 0); - std::fill(zero_buf, zero_buf + buf_len, 0); - - rc41.encrypt(buf, buf_len); - rc42.decrypt(buf, buf_len); - TEST_CHECK(std::equal(buf, buf + buf_len, zero_buf)); - - rc42.encrypt(buf, buf_len); - rc41.decrypt(buf, buf_len); - TEST_CHECK(std::equal(buf, buf + buf_len, zero_buf)); - - delete[] buf; - delete[] zero_buf; - } - + test_enc_handler(&rc41, &rc42); +#ifdef TORRENT_USE_OPENSSL + fprintf(stderr, "testing AES-256 handler\n"); + char key1[32]; + std::generate(key1, key1 + 32, &std::rand); + aes256_handler aes1; + aes1.set_incoming_key((const unsigned char*)key1, 32); + aes256_handler aes2; + aes2.set_incoming_key((const unsigned char*)key1, 32); + test_enc_handler(&aes1, &aes2); +#endif + + test_transfer(pe_settings::enabled, pe_settings::both, false, true); + test_transfer(pe_settings::enabled, pe_settings::both, true, true); + + return 0; + test_transfer(pe_settings::disabled); test_transfer(pe_settings::forced, pe_settings::plaintext);