From 1681981fe8c57a9250f417cbd6287df9f9362de6 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Sun, 28 Feb 2016 08:01:04 -0800 Subject: [PATCH] Add WPA2Decrypter callback interface --- CMakeLists.txt | 6 +++ cmake/appveyor.yml | 2 +- include/tins/config.h.in | 3 ++ include/tins/crypto.h | 64 +++++++++++++++++++++++++ src/crypto.cpp | 31 +++++++++++- tests/src/wpa2_decrypt.cpp | 96 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 200 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8adce00..6d7c0ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,6 +131,12 @@ ELSE() MESSAGE(STATUS "Disabling ACK tracking support") ENDIF() +OPTION(LIBTINS_ENABLE_WPA2_CALLBACKS "Enable WPA2 callback interface" ON) +IF(LIBTINS_ENABLE_WPA2_CALLBACKS AND TINS_HAVE_WPA2_DECRYPTION AND TINS_HAVE_CXX11) + SET(STATUS "Enabling WPA2 callback interface") + SET(TINS_HAVE_WPA2_CALLBACKS ON) +ENDIF() + # Use pcap_sendpacket to send l2 packets rather than raw sockets IF(WIN32) SET(USE_PCAP_SENDPACKET_DEFAULT ON) diff --git a/cmake/appveyor.yml b/cmake/appveyor.yml index a39ad02..be0d8b6 100644 --- a/cmake/appveyor.yml +++ b/cmake/appveyor.yml @@ -19,7 +19,7 @@ before_build: - mkdir build - cd build - if "%platform%"=="x64" ( set GENERATOR="Visual Studio 12 Win64" ) else ( set GENERATOR="Visual Studio 12" ) -- cmake .. -G %GENERATOR% -DPCAP_ROOT_DIR=c:\WpdPack -DLIBTINS_BUILD_SHARED=0 +- cmake .. -G %GENERATOR% -DPCAP_ROOT_DIR=c:\WpdPack -DLIBTINS_BUILD_SHARED=0 -DLIBTINS_ENABLE_WPA2=0 build: project: C:/projects/libtins/build/libtins.sln verbosity: minimal diff --git a/include/tins/config.h.in b/include/tins/config.h.in index 2448adc..b930a41 100644 --- a/include/tins/config.h.in +++ b/include/tins/config.h.in @@ -19,4 +19,7 @@ /* Have GCC builtin swap */ #cmakedefine TINS_HAVE_GCC_BUILTIN_SWAP +/* Have WPA2Decrypter callbacks */ +#cmakedefine TINS_HAVE_WPA2_CALLBACKS + #endif // TINS_CONFIG_H diff --git a/include/tins/crypto.h b/include/tins/crypto.h index 66d0037..850f831 100644 --- a/include/tins/crypto.h +++ b/include/tins/crypto.h @@ -36,6 +36,9 @@ #include #include #include +#ifdef TINS_HAVE_WPA2_CALLBACKS + #include +#endif // TINS_HAVE_WPA2_CALLBACKS #include "utils.h" #include "snap.h" #include "rawpdu.h" @@ -160,8 +163,15 @@ public: * \return The generated PMK. */ const pmk_type& pmk() const; + + /** + * \brief Getter for the SSID + * \return The access point's SSID + */ + const std::string& ssid() const; private: pmk_type pmk_; + std::string ssid_; }; } // WPA2 @@ -275,6 +285,32 @@ public: */ typedef std::map keys_map; + #ifdef TINS_HAVE_WPA2_CALLBACKS + + /** + * \brief The type used to store the callback type used when a new access + * point is found. + * + * The first argument to the function will be the access point's SSID and + * the second one its BSSID. + */ + typedef std::function ap_found_callback_type; + + /** + * The type used to store the callback type used when a new handshake + * is captured. + * + * The first argument to the function will be the access point's SSID and + * the second one its BSSID. The third argument will be the client's hardware + * address. + */ + typedef std::function handshake_captured_callback_type; + + #endif // TINS_HAVE_WPA2_CALLBACKS + /** * \brief Adds an access points's information. * @@ -353,6 +389,30 @@ public: */ bool decrypt(PDU& pdu); + #ifdef TINS_HAVE_WPA2_CALLBACKS + /** + * \brief Sets the handshake captured callback + * + * This callback will be executed every time a new handshake is captured. + * + * \sa handshake_captured_callback_type + * \param callback The new callback to be set + */ + void handshake_captured_callback(const handshake_captured_callback_type& callback); + + /** + * \brief Sets the access point found callback + * + * This callback will be executed every time a new access point is found, that's + * advertising an SSID added when calling add_ap_data. + * + * \sa ap_found_callback_type + * \param callback The new callback to be set + */ + void ap_found_callback(const ap_found_callback_type& callback); + + #endif // TINS_HAVE_WPA2_CALLBACKS + /** * \brief Getter for the keys on this decrypter * @@ -381,6 +441,10 @@ private: pmks_map pmks_; bssids_map aps_; keys_map keys_; + #ifdef TINS_HAVE_WPA2_CALLBACKS + handshake_captured_callback_type handshake_captured_callback_; + ap_found_callback_type ap_found_callback_; + #endif // TINS_HAVE_WPA2_CALLBACKS }; #endif // TINS_HAVE_WPA2_DECRYPTION diff --git a/src/crypto.cpp b/src/crypto.cpp index 4dd9cfa..4abbf84 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -491,7 +491,7 @@ bool SessionKeys::uses_ccmp() const { // supplicant_data SupplicantData::SupplicantData(const string& psk, const string& ssid) -: pmk_(SessionKeys::PMK_SIZE) { +: pmk_(SessionKeys::PMK_SIZE), ssid_(ssid) { PKCS5_PBKDF2_HMAC_SHA1( psk.c_str(), psk.size(), @@ -506,6 +506,11 @@ SupplicantData::SupplicantData(const string& psk, const string& ssid) const SupplicantData::pmk_type& SupplicantData::pmk() const { return pmk_; } + +const string& SupplicantData::ssid() const { + return ssid_; +} + } // namespace WPA2 void WPA2Decrypter::add_ap_data(const string& psk, const string& ssid) { @@ -525,6 +530,12 @@ void WPA2Decrypter::add_access_point(const string& ssid, const address_type& add throw runtime_error("Supplicant data not registered"); } aps_.insert(make_pair(addr, it->second)); + + #ifdef TINS_HAVE_WPA2_CALLBACKS + if (ap_found_callback_) { + ap_found_callback_(ssid, addr); + } + #endif // TINS_HAVE_WPA2_CALLBACKS } void WPA2Decrypter::add_decryption_keys(const addr_pair& addresses, @@ -540,6 +551,12 @@ void WPA2Decrypter::try_add_keys(const Dot11Data& dot11, const RSNHandshake& hs) try { SessionKeys session(hs, it->second.pmk()); keys_[addr_p] = session; + #ifdef TINS_HAVE_WPA2_CALLBACKS + if (handshake_captured_callback_) { + handshake_captured_callback_(it->second.ssid(), addr_p.first, + addr_p.second); + } + #endif // TINS_HAVE_WPA2_CALLBACKS } catch(WPA2::invalid_handshake&) { @@ -629,6 +646,18 @@ bool WPA2Decrypter::decrypt(PDU& pdu) { return false; } +#ifdef TINS_HAVE_WPA2_CALLBACKS + +void WPA2Decrypter::handshake_captured_callback(const handshake_captured_callback_type& callback) { + handshake_captured_callback_ = callback; +} + +void WPA2Decrypter::ap_found_callback(const ap_found_callback_type& callback) { + ap_found_callback_ = callback; +} + +#endif // TINS_HAVE_WPA2_CALLBACKS + #endif // TINS_HAVE_WPA2_DECRYPTION } // namespace Crypto diff --git a/tests/src/wpa2_decrypt.cpp b/tests/src/wpa2_decrypt.cpp index 935b7d1..e91129c 100644 --- a/tests/src/wpa2_decrypt.cpp +++ b/tests/src/wpa2_decrypt.cpp @@ -8,22 +8,59 @@ #include #include "crypto.h" #include "radiotap.h" +#include "dot11/dot11_data.h" #include "udp.h" #include "tcp.h" using namespace Tins; +using std::string; +using std::vector; + class WPA2DecryptTest : public testing::Test { public: + typedef HWAddress<6> address_type; static const uint8_t ccmp_packets[7][652]; static const uint8_t tkip_packets[7][211]; static const size_t ccmp_packets_size[], tkip_packets_size[]; + + struct handshake { + handshake(const string& ssid, const address_type& bssid, const address_type& client_hw) + : ssid(ssid), bssid(bssid), client_hw(client_hw) { + + } + + string ssid; + address_type bssid; + address_type client_hw; + }; + + struct ap_data { + ap_data(const string& ssid, const address_type& bssid) + : ssid(ssid), bssid(bssid) { + + } + + string ssid; + address_type bssid; + }; void check_ccmp_packet5(const PDU& pdu); void check_ccmp_packet6(const PDU& pdu); void check_tkip_packet5(const PDU& pdu); void check_tkip_packet6(const PDU& pdu); + + void handshake_captured(const string& ssid, const address_type& bssid, const address_type& client_hw) { + handshakes_.push_back(handshake(ssid, bssid, client_hw)); + } + + void ap_found(const string& ssid, const address_type& bssid) { + access_points_.push_back(ap_data(ssid, bssid)); + } + + vector handshakes_; + vector access_points_; }; // packet taken from aircrack's site. @@ -283,4 +320,63 @@ TEST_F(WPA2DecryptTest, DecryptCCMPAndTKIPWithoutUsingBeacon) { } } +#ifdef TINS_HAVE_WPA2_CALLBACKS + +TEST_F(WPA2DecryptTest, HandshakeCapturedCallback) { + using namespace std::placeholders; + + Crypto::WPA2Decrypter decrypter; + decrypter.add_ap_data("libtinstest", "NODO", "00:1b:11:d2:1b:eb"); + decrypter.add_ap_data("Induction", "Coherer", "00:0c:41:82:b2:55"); + decrypter.handshake_captured_callback(std::bind(&WPA2DecryptTest::handshake_captured, + this, _1, _2, _3)); + for(size_t i = 1; i < 7; ++i) { + RadioTap radio(ccmp_packets[i], ccmp_packets_size[i]); + decrypter.decrypt(radio); + } + for(size_t i = 1; i < 7; ++i) { + RadioTap radio(tkip_packets[i], tkip_packets_size[i]); + decrypter.decrypt(radio); + } + + ASSERT_EQ(2, handshakes_.size()); + handshake hs = handshakes_[0]; + EXPECT_EQ(hs.ssid, "Coherer"); + EXPECT_EQ(address_type("00:0d:93:82:36:3a"), hs.client_hw); + EXPECT_EQ(address_type("00:0c:41:82:b2:55"), hs.bssid); + + hs = handshakes_[1]; + EXPECT_EQ(hs.ssid, "NODO"); + EXPECT_EQ(address_type("94:0c:6d:8f:93:88"), hs.client_hw); + EXPECT_EQ(address_type("00:1b:11:d2:1b:eb"), hs.bssid); +} + +TEST_F(WPA2DecryptTest, AccessPointFoundCallback) { + using namespace std::placeholders; + + Crypto::WPA2Decrypter decrypter; + decrypter.add_ap_data("libtinstest", "NODO"); + decrypter.add_ap_data("Induction", "Coherer"); + decrypter.ap_found_callback(std::bind(&WPA2DecryptTest::ap_found, this, _1, _2)); + for(size_t i = 0; i < 7; ++i) { + RadioTap radio(ccmp_packets[i], ccmp_packets_size[i]); + decrypter.decrypt(radio); + } + for(size_t i = 0; i < 7; ++i) { + RadioTap radio(tkip_packets[i], tkip_packets_size[i]); + decrypter.decrypt(radio); + } + + ASSERT_EQ(2, access_points_.size()); + ap_data data = access_points_[0]; + EXPECT_EQ("Coherer", data.ssid); + EXPECT_EQ(address_type("00:0c:41:82:b2:55"), data.bssid); + + data = access_points_[1]; + EXPECT_EQ("NODO", data.ssid); + EXPECT_EQ(address_type("00:1b:11:d2:1b:eb"), data.bssid); +} + +#endif // TINS_HAVE_WPA2_CALLBACKS + #endif // defined(TINS_HAVE_DOT11) && defined(TINS_HAVE_WPA2_DECRYPTION)