merged SSL unit tests from RC_0_16
This commit is contained in:
parent
93d7d89ced
commit
1dfc93aad8
|
@ -0,0 +1,60 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 15441482899666218084 (0xd64b2ae27fdfac64)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=test CA
|
||||
Validity
|
||||
Not Before: Dec 15 20:42:32 2013 GMT
|
||||
Not After : Dec 15 20:42:32 2014 GMT
|
||||
Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=*
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:df:4a:a7:c3:0b:23:3a:16:e3:aa:66:5d:f4:57:
|
||||
54:f5:94:92:55:4e:38:f3:32:a9:1d:7a:cf:f4:35:
|
||||
4e:25:28:7f:3a:3d:f1:c9:9f:99:f7:42:fc:8c:8c:
|
||||
ac:f3:74:91:0e:80:5c:1e:8c:ff:26:94:1f:7e:2d:
|
||||
d2:55:33:e9:69:e3:74:03:a0:78:43:45:25:5f:bb:
|
||||
7d:59:e4:8c:46:ff:7a:4a:7f:51:89:71:f1:6b:07:
|
||||
24:9b:0b:37:a5:53:c4:ed:57:05:cf:08:2c:64:43:
|
||||
95:a3:93:6d:bd:98:f5:57:4b:7b:7a:c9:3f:47:a5:
|
||||
7c:20:fa:91:55:58:df:24:2d
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Comment:
|
||||
OpenSSL Generated Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
EA:FC:F9:50:A2:50:BB:33:DC:FC:15:17:CE:68:D2:3C:03:FD:5B:FF
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:36:A1:4C:8C:07:84:35:8E:76:DF:22:90:AD:63:21:F5:4A:BD:34:E4
|
||||
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
2c:69:84:c4:cd:0d:43:20:6c:7a:60:9d:9d:ed:9c:e0:22:10:
|
||||
8c:d1:9e:08:78:bd:ad:d5:03:9b:b2:4f:93:a1:6f:5b:38:47:
|
||||
ed:30:9a:2e:e8:ea:64:e7:6c:dd:df:fd:40:29:4d:0f:2e:e3:
|
||||
1f:5c:b8:ac:f4:37:28:05:22:35:70:3a:04:63:f8:88:d6:cb:
|
||||
33:f2:bc:05:b4:d8:4f:0d:eb:4c:e1:a6:55:69:3b:c3:90:7a:
|
||||
d5:ef:2b:0d:91:17:67:8e:08:79:bb:7d:50:f9:1f:36:91:c8:
|
||||
22:0d:d6:f9:f2:5e:c5:66:bf:27:bc:b2:32:4d:2a:cd:7c:a4:
|
||||
2a:ee
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICoTCCAgqgAwIBAgIJANZLKuJ/36xkMA0GCSqGSIb3DQEBBQUAMFcxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQxEDAOBgNVBAMMB3Rlc3QgQ0EwHhcNMTMxMjE1MjA0MjMy
|
||||
WhcNMTQxMjE1MjA0MjMyWjBRMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1T
|
||||
dGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQowCAYDVQQD
|
||||
DAEqMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfSqfDCyM6FuOqZl30V1T1
|
||||
lJJVTjjzMqkdes/0NU4lKH86PfHJn5n3QvyMjKzzdJEOgFwejP8mlB9+LdJVM+lp
|
||||
43QDoHhDRSVfu31Z5IxG/3pKf1GJcfFrBySbCzelU8TtVwXPCCxkQ5Wjk229mPVX
|
||||
S3t6yT9HpXwg+pFVWN8kLQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIB
|
||||
DQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU6vz5
|
||||
UKJQuzPc/BUXzmjSPAP9W/8wHwYDVR0jBBgwFoAUNqFMjAeENY523yKQrWMh9Uq9
|
||||
NOQwDQYJKoZIhvcNAQEFBQADgYEALGmExM0NQyBsemCdne2c4CIQjNGeCHi9rdUD
|
||||
m7JPk6FvWzhH7TCaLujqZOds3d/9QClNDy7jH1y4rPQ3KAUiNXA6BGP4iNbLM/K8
|
||||
BbTYTw3rTOGmVWk7w5B61e8rDZEXZ44Iebt9UPkfNpHIIg3W+fJexWa/J7yyMk0q
|
||||
zXykKu4=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,17 @@
|
|||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIsLJkydWZi7ACAggA
|
||||
MBQGCCqGSIb3DQMHBAicrqPGKfsAVQSCAoBNO9Cjpr1Zuw5jKLyVkGFfTLLtvT4Z
|
||||
b+zAYNiqhD3idw8wSkPKgs4JXbkCJtoOMUyUXPNSczsifjbIDewZbYBOxZXHFgtc
|
||||
JS1nKCpwy5vktWzhl+PHhtJDOdAehK2igwkv5TwwXF4jJYc4pUNMjwebygJImqeG
|
||||
9LhCd2LjBb3tk1qy5lqGRL4X+FQslSp0IGGNiBehQDskrAXE1rf8NoscJFO4UJfb
|
||||
M0Ad59MVB620xgQ3rUxQ40RR2ZYZQ+o0/Bhe2hvSZyoeB+M1DnKbCvgi45qWYy25
|
||||
NzT7wohQb1OAJ3EEYhRgnW3E4NtuUuXxUyRAK9M94dxlOR//QE2W32V7evV9Elgd
|
||||
H16BcdObKqBycuqdLHkJ7jGcwJXbCD4eK+T9iLccH9G3/FO1n98yLQ67AnSJ0IaV
|
||||
wBOENhJ14qIqr3dwFZIcEo/TzQqKApKef6GULlHQ7Ime7spJvVr9uNcnLa6yq8y0
|
||||
2EjeAKitEtgFpLvt0V5xd+NPzyznsKybbT0va39/C1KA94eqniOG3rc8dDGsjd6k
|
||||
ZFyNXR/aiT/FLmP84qukcSFsap+6PSyFJv5VVMcB4V2RtxPU5sD0HirYOpY/Wuj9
|
||||
qG2ONflvh2HrGEn5snmD7Bm7k0eidQBOWCiYn8rNHVCjBV1aN+LxYtDAFHhzLKUB
|
||||
BXD6wZoHzHZof5jdLbr6S58KHxQYnd0vFlC3n42HNtQrbWNgaFs33z1PrnGsgKma
|
||||
bCJHBk4+e35ee8aGCpv9zEDGc1l5l8zpc4+SSOOkChKdfihdyj1+HaWx9q2StEPz
|
||||
49G76d3X82SDpRYxchwl2S30eH3b+X2vLsQg3Z2gRi+LYOBFXD0tUuXF
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
|
@ -41,9 +41,11 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "setup_transfer.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <boost/asio/connect.hpp>
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
#include <boost/asio/ssl/error.hpp> // for asio::error::get_ssl_category()
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#endif
|
||||
|
||||
using namespace libtorrent;
|
||||
|
@ -154,6 +156,7 @@ void test_ssl(int test_idx, bool use_utp)
|
|||
file.close();
|
||||
|
||||
add_torrent_params addp;
|
||||
addp.save_path = ".";
|
||||
addp.flags &= ~add_torrent_params::flag_paused;
|
||||
addp.flags &= ~add_torrent_params::flag_auto_managed;
|
||||
|
||||
|
@ -165,7 +168,7 @@ void test_ssl(int test_idx, bool use_utp)
|
|||
peer_errors = 0;
|
||||
|
||||
boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0
|
||||
, true, false, true, "_ssl", 16 * 1024, &t, false, NULL, true, test.use_ssl_ports);
|
||||
, true, false, true, "_ssl", 16 * 1024, &t, false, &addp, true, test.use_ssl_ports);
|
||||
|
||||
if (test.seed_has_cert)
|
||||
{
|
||||
|
@ -253,10 +256,283 @@ void test_ssl(int test_idx, bool use_utp)
|
|||
p2 = ses2.abort();
|
||||
}
|
||||
|
||||
std::string password_callback(int length, boost::asio::ssl::context::password_purpose p
|
||||
, std::string pw)
|
||||
{
|
||||
if (p != boost::asio::ssl::context::for_reading) return "";
|
||||
return pw;
|
||||
}
|
||||
|
||||
struct attack_t
|
||||
{
|
||||
// flags controlling the connection attempt
|
||||
boost::uint32_t flags;
|
||||
// whether or not we expect to be able to connect
|
||||
bool expect;
|
||||
};
|
||||
|
||||
enum attack_flags_t
|
||||
{
|
||||
valid_certificate = 1,
|
||||
invalid_certificate = 2,
|
||||
valid_sni_hash = 4,
|
||||
invalid_sni_hash = 8,
|
||||
valid_bittorrent_hash = 16,
|
||||
};
|
||||
|
||||
attack_t attacks[] =
|
||||
{
|
||||
// positive test
|
||||
{ valid_certificate | valid_sni_hash | valid_bittorrent_hash, true},
|
||||
|
||||
// SNI
|
||||
{ valid_certificate | invalid_sni_hash | valid_bittorrent_hash, false},
|
||||
{ valid_certificate | valid_bittorrent_hash, false},
|
||||
|
||||
// certificate
|
||||
{ valid_sni_hash | valid_bittorrent_hash, false},
|
||||
{ invalid_certificate | valid_sni_hash | valid_bittorrent_hash, false},
|
||||
|
||||
// bittorrent hash
|
||||
{ valid_certificate | valid_sni_hash, false},
|
||||
};
|
||||
|
||||
const int num_attacks = sizeof(attacks)/sizeof(attacks[0]);
|
||||
|
||||
bool try_connect(session& ses1, int port
|
||||
, boost::intrusive_ptr<torrent_info> const& t, boost::uint32_t flags)
|
||||
{
|
||||
using boost::asio::ssl::context;
|
||||
|
||||
fprintf(stderr, "\nMALICIOUS PEER TEST: ");
|
||||
if (flags & invalid_certificate) fprintf(stderr, "invalid-certificate ");
|
||||
else if (flags & valid_certificate) fprintf(stderr, "valid-certificate ");
|
||||
else fprintf(stderr, "no-certificate ");
|
||||
|
||||
if (flags & invalid_sni_hash) fprintf(stderr, "invalid-SNI-hash ");
|
||||
else if (flags & valid_sni_hash) fprintf(stderr, "valid-SNI-hash ");
|
||||
else fprintf(stderr, "no-SNI-hash ");
|
||||
|
||||
if (flags & valid_bittorrent_hash) fprintf(stderr, "valid-bittorrent-hash ");
|
||||
else fprintf(stderr, "invalid-bittorrent-hash ");
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
error_code ec;
|
||||
boost::asio::io_service ios;
|
||||
|
||||
// create the SSL context for this torrent. We need to
|
||||
// inject the root certificate, and no other, to
|
||||
// verify other peers against
|
||||
context ctx(ios, context::sslv23);
|
||||
|
||||
ctx.set_options(context::default_workarounds
|
||||
| boost::asio::ssl::context::no_sslv2
|
||||
| boost::asio::ssl::context::single_dh_use);
|
||||
|
||||
// we're a malicious peer, we don't have any interest
|
||||
// in verifying peers
|
||||
ctx.set_verify_mode(context::verify_none, ec);
|
||||
if (ec)
|
||||
{
|
||||
fprintf(stderr, "Failed to set SSL verify mode: %s\n"
|
||||
, ec.message().c_str());
|
||||
TEST_CHECK(!ec);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string certificate = combine_path("ssl", "peer_certificate.pem");
|
||||
std::string private_key = combine_path("ssl", "peer_private_key.pem");
|
||||
std::string dh_params = combine_path("ssl", "dhparams.pem");
|
||||
|
||||
if (flags & invalid_certificate)
|
||||
{
|
||||
certificate = combine_path("ssl", "invalid_peer_certificate.pem");
|
||||
private_key = combine_path("ssl", "invalid_peer_private_key.pem");
|
||||
}
|
||||
|
||||
// TODO: test using a signed certificate with the wrong info-hash in DN
|
||||
|
||||
if (flags & (valid_certificate | invalid_certificate))
|
||||
{
|
||||
ctx.set_password_callback(boost::bind(&password_callback, _1, _2, "test"), ec);
|
||||
if (ec)
|
||||
{
|
||||
fprintf(stderr, "Failed to set certificate password callback: %s\n"
|
||||
, ec.message().c_str());
|
||||
TEST_CHECK(!ec);
|
||||
return false;
|
||||
}
|
||||
ctx.use_certificate_file(certificate, context::pem, ec);
|
||||
if (ec)
|
||||
{
|
||||
fprintf(stderr, "Failed to set certificate file: %s\n"
|
||||
, ec.message().c_str());
|
||||
TEST_CHECK(!ec);
|
||||
return false;
|
||||
}
|
||||
ctx.use_private_key_file(private_key, context::pem, ec);
|
||||
if (ec)
|
||||
{
|
||||
fprintf(stderr, "Failed to set private key: %s\n"
|
||||
, ec.message().c_str());
|
||||
TEST_CHECK(!ec);
|
||||
return false;
|
||||
}
|
||||
ctx.use_tmp_dh_file(dh_params, ec);
|
||||
if (ec)
|
||||
{
|
||||
fprintf(stderr, "Failed to set DH params: %s\n"
|
||||
, ec.message().c_str());
|
||||
TEST_CHECK(!ec);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_sock(ios, ctx);
|
||||
|
||||
ssl_sock.lowest_layer().connect(tcp::endpoint(
|
||||
address_v4::from_string("127.0.0.1"), port), ec);
|
||||
print_alerts(ses1, "ses1", true, true, true, &on_alert);
|
||||
|
||||
if (ec)
|
||||
{
|
||||
fprintf(stderr, "Failed to connect: %s\n"
|
||||
, ec.message().c_str());
|
||||
TEST_CHECK(!ec);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags & valid_sni_hash)
|
||||
{
|
||||
std::string name = to_hex(t->info_hash().to_string());
|
||||
fprintf(stderr, "SNI: %s\n", name.c_str());
|
||||
SSL_set_tlsext_host_name(ssl_sock.native_handle(), name.c_str());
|
||||
}
|
||||
else if (flags & invalid_sni_hash)
|
||||
{
|
||||
char const hex_alphabet[] = "0123456789abcdef";
|
||||
std::string name;
|
||||
name.reserve(40);
|
||||
for (int i = 0; i < 40; ++i)
|
||||
name += hex_alphabet[rand() % 16];
|
||||
|
||||
fprintf(stderr, "SNI: %s\n", name.c_str());
|
||||
SSL_set_tlsext_host_name(ssl_sock.native_handle(), name.c_str());
|
||||
}
|
||||
|
||||
ssl_sock.handshake(asio::ssl::stream_base::client, ec);
|
||||
|
||||
print_alerts(ses1, "ses1", true, true, true, &on_alert);
|
||||
if (ec)
|
||||
{
|
||||
fprintf(stderr, "Failed SSL handshake: %s\n"
|
||||
, ec.message().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
char handshake[] = "\x13" "BitTorrent protocol\0\0\0\0\0\0\0\x04"
|
||||
" " // space for info-hash
|
||||
"aaaaaaaaaaaaaaaaaaaa" // peer-id
|
||||
"\0\0\0\x01\x02"; // interested
|
||||
|
||||
// fill in the info-hash
|
||||
if (flags & valid_bittorrent_hash)
|
||||
{
|
||||
std::memcpy(handshake + 28, &t->info_hash()[0], 20);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: also test using a hash that refers to a valid torrent
|
||||
// but that differs from the SNI hash
|
||||
std::generate(handshake + 28, handshake + 48, &rand);
|
||||
}
|
||||
|
||||
// fill in the peer-id
|
||||
std::generate(handshake + 48, handshake + 68, &rand);
|
||||
boost::asio::write(ssl_sock, libtorrent::asio::buffer(handshake, (sizeof(handshake) - 1)), ec);
|
||||
if (ec)
|
||||
{
|
||||
fprintf(stderr, "failed to write bittorrent handshake: %s\n"
|
||||
, ec.message().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[68];
|
||||
boost::asio::read(ssl_sock, libtorrent::asio::buffer(buf, sizeof(buf)), ec);
|
||||
if (ec)
|
||||
{
|
||||
fprintf(stderr, "failed to read bittorrent handshake: %s\n"
|
||||
, ec.message().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(buf, "\x13" "BitTorrent protocol", 20) != 0)
|
||||
{
|
||||
fprintf(stderr, "invalid bittorrent handshake\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(buf + 28, &t->info_hash()[0], 20) != 0)
|
||||
{
|
||||
fprintf(stderr, "invalid info-hash in bittorrent handshake\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(stderr, "successfully connected over SSL and shook hand over bittorrent\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void test_malicious_peer()
|
||||
{
|
||||
error_code ec;
|
||||
remove_all("tmp3_ssl", ec);
|
||||
|
||||
// set up session
|
||||
session ses1(fingerprint("LT", 0, 1, 0, 0)
|
||||
, std::make_pair(48075, 49000), "0.0.0.0", 0, alert_mask);
|
||||
wait_for_listen(ses1, "ses1");
|
||||
session_settings sett;
|
||||
sett.ssl_listen = 1024 + rand() % 50000;
|
||||
ses1.set_settings(sett);
|
||||
|
||||
// create torrent
|
||||
create_directory("tmp3_ssl", ec);
|
||||
std::ofstream file("tmp3_ssl/temporary");
|
||||
boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file
|
||||
, 16 * 1024, 13, false, "ssl/root_ca_cert.pem");
|
||||
file.close();
|
||||
|
||||
add_torrent_params addp;
|
||||
addp.save_path = ".";
|
||||
addp.flags &= ~add_torrent_params::flag_paused;
|
||||
addp.flags &= ~add_torrent_params::flag_auto_managed;
|
||||
addp.ti = t;
|
||||
|
||||
torrent_handle tor1 = ses1.add_torrent(addp, ec);
|
||||
|
||||
tor1.set_ssl_certificate(
|
||||
combine_path("ssl", "peer_certificate.pem")
|
||||
, combine_path("ssl", "peer_private_key.pem")
|
||||
, combine_path("ssl", "dhparams.pem")
|
||||
, "test");
|
||||
|
||||
wait_for_listen(ses1, "ses1");
|
||||
|
||||
for (int i = 0; i < num_attacks; ++i)
|
||||
{
|
||||
bool success = try_connect(ses1, sett.ssl_listen, t, attacks[i].flags);
|
||||
TEST_EQUAL(attacks[i].expect, success);
|
||||
}
|
||||
}
|
||||
|
||||
int test_main()
|
||||
{
|
||||
using namespace libtorrent;
|
||||
|
||||
test_malicious_peer();
|
||||
|
||||
// No support for SSL/uTP yet, so always pass in false
|
||||
for (int i = 0; i < sizeof(test_config)/sizeof(test_config[0]); ++i)
|
||||
test_ssl(i, false);
|
||||
|
|
Loading…
Reference in New Issue