537 lines
17 KiB
C++
537 lines
17 KiB
C++
/*
|
|
* Copyright (c) 2017, Matias Fontanini
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include <tins/config.h>
|
|
|
|
#if !defined(TINS_CRYPTO_H) && defined(TINS_HAVE_DOT11)
|
|
#define TINS_CRYPTO_H
|
|
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
#ifdef TINS_HAVE_WPA2_CALLBACKS
|
|
#include <functional>
|
|
#endif // TINS_HAVE_WPA2_CALLBACKS
|
|
#include <tins/macros.h>
|
|
#include <tins/handshake_capturer.h>
|
|
|
|
namespace Tins {
|
|
|
|
class PDU;
|
|
class Dot11;
|
|
class Dot11Data;
|
|
class SNAP;
|
|
class RawPDU;
|
|
|
|
namespace Crypto {
|
|
|
|
#ifdef TINS_HAVE_WPA2_DECRYPTION
|
|
namespace WPA2 {
|
|
|
|
/**
|
|
* \brief Class that represents the keys used to decrypt a session.
|
|
*/
|
|
class TINS_API SessionKeys {
|
|
public:
|
|
/**
|
|
* The size of the Pairwise Master Key.
|
|
*/
|
|
static const size_t PMK_SIZE;
|
|
|
|
/**
|
|
* The size of the Pairwise Transient Key.
|
|
*/
|
|
static const size_t PTK_SIZE;
|
|
|
|
/**
|
|
* The type used to hold the PTK (this has to be PTK_SIZE bytes long).
|
|
*/
|
|
typedef std::vector<uint8_t> ptk_type;
|
|
|
|
/**
|
|
* The type used to hold the PMK (this has to be PMK_SIZE bytes long).
|
|
*/
|
|
typedef std::vector<uint8_t> pmk_type;
|
|
|
|
/**
|
|
* Default constructs a SessionKeys object.
|
|
*/
|
|
SessionKeys();
|
|
|
|
/**
|
|
* \brief Constructs an instance using the provided PTK and a flag
|
|
* indicating whether it should use ccmp.
|
|
*
|
|
* \param ptk The PTK to use.
|
|
* \param is_ccmp Indicates whether to use CCMP to decrypt this traffic.
|
|
*/
|
|
SessionKeys(const ptk_type& ptk, bool is_ccmp);
|
|
|
|
/**
|
|
* \brief Constructs an instance using a handshake and a PMK.
|
|
*
|
|
* This will internally construct the PTK from the input parameters.
|
|
*
|
|
* \param hs The handshake to use.
|
|
* \param pmk The PMK to use.
|
|
*/
|
|
SessionKeys(const RSNHandshake& hs, const pmk_type& pmk);
|
|
|
|
/**
|
|
* \brief Decrypts a unicast packet.
|
|
*
|
|
* \param dot11 The encrypted packet to decrypt.
|
|
* \param raw The raw layer on the packet to decrypt.
|
|
* \return A SNAP layer containing the decrypted traffic or a null pointer
|
|
* if decryption failed.
|
|
*/
|
|
SNAP* decrypt_unicast(const Dot11Data& dot11, RawPDU& raw) const;
|
|
|
|
/**
|
|
* \brief Gets the PTK for this session keys.
|
|
* \return The Pairwise Transcient Key.
|
|
*/
|
|
const ptk_type& get_ptk() const;
|
|
|
|
/**
|
|
* \brief Indicates whether CCMP is used to decrypt packets
|
|
* /return true iff CCMP is used.
|
|
*/
|
|
bool uses_ccmp() const;
|
|
private:
|
|
SNAP* ccmp_decrypt_unicast(const Dot11Data& dot11, RawPDU& raw) const;
|
|
SNAP* tkip_decrypt_unicast(const Dot11Data& dot11, RawPDU& raw) const;
|
|
|
|
ptk_type ptk_;
|
|
bool is_ccmp_;
|
|
};
|
|
|
|
/**
|
|
* \brief Represents a WPA2 supplicant's data.
|
|
*
|
|
* Objects of this class can be given the pre-shared key and the SSID
|
|
* of some access point, and this will generate the Pairwise Master Key
|
|
* from those parameters.
|
|
*/
|
|
class TINS_API SupplicantData {
|
|
public:
|
|
/**
|
|
* The type used to store the PMK.
|
|
*/
|
|
typedef SessionKeys::pmk_type pmk_type;
|
|
|
|
/**
|
|
* \brief Constructs a SupplicantData.
|
|
* \param psk The pre-shared key.
|
|
* \param ssid The access point's SSID.
|
|
*/
|
|
SupplicantData(const std::string& psk, const std::string& ssid);
|
|
|
|
/**
|
|
* \brief Getter for the PMK.
|
|
* \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
|
|
#endif // TINS_HAVE_WPA2_DECRYPTION
|
|
|
|
/**
|
|
* \brief Decrypts WEP-encrypted traffic.
|
|
*/
|
|
class TINS_API WEPDecrypter {
|
|
public:
|
|
typedef HWAddress<6> address_type;
|
|
|
|
/**
|
|
* \brief Constructs a WEPDecrypter object.
|
|
*/
|
|
WEPDecrypter();
|
|
|
|
/**
|
|
* \brief Adds a decryption password.
|
|
*
|
|
* \param addr The access point's BSSID.
|
|
* \param password The password which will be used to decrypt
|
|
* packets sent from and to the AP identifier by the BSSID addr.
|
|
*/
|
|
void add_password(const address_type& addr, const std::string& password);
|
|
|
|
/**
|
|
* \brief Removes a decryption password
|
|
*
|
|
* \param addr The BSSID of the access point.
|
|
*/
|
|
void remove_password(const address_type& addr);
|
|
|
|
/**
|
|
* \brief Decrypts the provided PDU.
|
|
*
|
|
* A Dot11Data PDU is looked up inside the provided PDU chain.
|
|
* If no such PDU exists or there is no password associated
|
|
* with the Dot11 packet's BSSID, then the PDU is left intact.
|
|
*
|
|
* Otherwise, the packet is decrypted using the given password.
|
|
* If the CRC found after decrypting is invalid, false is
|
|
* returned.
|
|
*
|
|
* \return false if no decryption was performed or decryption
|
|
* failed, true otherwise.
|
|
*/
|
|
bool decrypt(PDU& pdu);
|
|
private:
|
|
typedef std::map<address_type, std::string> passwords_type;
|
|
|
|
PDU* decrypt(RawPDU& raw, const std::string& password);
|
|
|
|
passwords_type passwords_;
|
|
std::vector<uint8_t> key_buffer_;
|
|
};
|
|
|
|
#ifdef TINS_HAVE_WPA2_DECRYPTION
|
|
/**
|
|
* \brief Decrypts WPA2-encrypted traffic.
|
|
*
|
|
* This class takes valid PSK and SSID tuples, captures client handshakes,
|
|
* and decrypts their traffic afterwards.
|
|
*/
|
|
class TINS_API WPA2Decrypter {
|
|
public:
|
|
/*
|
|
* \brief The type used to store Dot11 addresses.
|
|
*/
|
|
typedef HWAddress<6> address_type;
|
|
|
|
/**
|
|
* \brief Represents a pair of mac addresses.
|
|
*
|
|
* This is used to identify a host and the access point to which
|
|
* it is connected. The first element in the pair will always de
|
|
* lower or equal than the second one, so that given any host and
|
|
* the access point it's connected to, we can uniquely identify
|
|
* it with an address pair.
|
|
*/
|
|
typedef std::pair<address_type, address_type> addr_pair;
|
|
|
|
/**
|
|
* \brief Maps an address pair to the session keys.
|
|
*
|
|
* This type associates an address pair (host, access point) with the
|
|
* session keys, as generated using the packets seen on a handshake.
|
|
*
|
|
* \sa addr_pair
|
|
*/
|
|
typedef std::map<addr_pair, WPA2::SessionKeys> 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<void(const std::string&,
|
|
const address_type&)> 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<void(const std::string&,
|
|
const address_type&,
|
|
const address_type&)> handshake_captured_callback_type;
|
|
|
|
#endif // TINS_HAVE_WPA2_CALLBACKS
|
|
|
|
/**
|
|
* \brief Adds an access points's information.
|
|
*
|
|
* This associates an SSID with a PSK, and allows the decryption of
|
|
* any BSSIDs that broadcast the same SSID.
|
|
*
|
|
* The decrypter will inspect beacon frames, looking for SSID tags
|
|
* that contain the given SSID.
|
|
*
|
|
* Note that using this overload, the decryption of data frames and
|
|
* handshake capturing will be disabled until any access point
|
|
* broadcasts the provided SSID(this shouldn't take long at all).
|
|
* If this is not the desired behaviour, then you should check out
|
|
* the ovther add_ap_data overload.
|
|
*
|
|
* \param psk The PSK associated with the SSID.
|
|
* \param ssid The network's SSID.
|
|
*/
|
|
void add_ap_data(const std::string& psk, const std::string& ssid);
|
|
|
|
/**
|
|
* \brief Adds a access points's information, including its BSSID.
|
|
*
|
|
* This overload can be used if the BSSID associated with this SSID is
|
|
* known beforehand. The addr parameter indicates which specific BSSID
|
|
* is associated to the SSID.
|
|
*
|
|
* Note that if any other access point broadcasts the provided SSID,
|
|
* it will be taken into account as well.
|
|
*
|
|
* \param psk The PSK associated with this SSID.
|
|
* \param ssid The network's SSID.
|
|
* \param addr The access point's BSSID.
|
|
*/
|
|
void add_ap_data(const std::string& psk,
|
|
const std::string& ssid,
|
|
const address_type& addr);
|
|
|
|
/**
|
|
* \brief Explicitly add decryption keys.
|
|
*
|
|
* This method associates a pair (host, access point) with the given decryption keys.
|
|
* All encrypted packets sent between the given addresses will be decrypted using the
|
|
* provided keys.
|
|
*
|
|
* This method shouldn't normally be required. The WPA2Decrypter will be waiting for
|
|
* handshakes and will automatically extract the session keys, decrypting all
|
|
* encrypted packets with them. You should only use this method if for some reason
|
|
* you know the actual keys being used (because you checked and stored the keys_map
|
|
* somewhere).
|
|
*
|
|
* The actual order of the addresses doesn't matter, this method will make sure
|
|
* they're sorted.
|
|
*
|
|
* \param addresses The address pair (host, access point) to associate.
|
|
* \param session_keys The keys to use when decrypting messages sent between the
|
|
* given addresses.
|
|
*/
|
|
void add_decryption_keys(const addr_pair& addresses,
|
|
const WPA2::SessionKeys& session_keys);
|
|
|
|
/**
|
|
* \brief Decrypts the provided PDU.
|
|
*
|
|
* A Dot11Data PDU is looked up inside the provided PDU chain.
|
|
* If no such PDU exists or no PSK was associated with the SSID
|
|
* broadcasted by the Dot11 packet's BSSID, or no EAPOL handshake
|
|
* was captured for the client involved in the communication,
|
|
* then the PDU is left intact.
|
|
*
|
|
* Otherwise, the packet is decrypted using the generated PTK.
|
|
* If the resulting MIC is invalid, then the packet is left intact.
|
|
*
|
|
* \return false if no decryption was performed, or the decryption
|
|
* failed, true otherwise.
|
|
*/
|
|
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
|
|
*
|
|
* The returned map will be populated every time a new, complete, handshake
|
|
* is captured.
|
|
*
|
|
* \return The WPA2Decrypter keys map.
|
|
*/
|
|
const keys_map& get_keys() const;
|
|
private:
|
|
typedef std::map<std::string, WPA2::SupplicantData> pmks_map;
|
|
typedef std::map<address_type, WPA2::SupplicantData> bssids_map;
|
|
|
|
void try_add_keys(const Dot11Data& dot11, const RSNHandshake& hs);
|
|
addr_pair make_addr_pair(const address_type& addr1, const address_type& addr2) {
|
|
return (addr1 < addr2) ?
|
|
std::make_pair(addr1, addr2) :
|
|
std::make_pair(addr2, addr1);
|
|
}
|
|
addr_pair extract_addr_pair(const Dot11Data& dot11);
|
|
addr_pair extract_addr_pair_dst(const Dot11Data& dot11);
|
|
bssids_map::const_iterator find_ap(const Dot11Data& dot11);
|
|
void add_access_point(const std::string& ssid, const address_type& addr);
|
|
|
|
RSNHandshakeCapturer capturer_;
|
|
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
|
|
|
|
/**
|
|
* \brief Pluggable decrypter object which can be used to decrypt
|
|
* data on sniffing sessions.
|
|
*
|
|
* This class holds a decrypter object and a functor, and implements
|
|
* a suitable operator() to be used on BaseSniffer::sniff_loop, which
|
|
* decrypts packets and forwards them to the given functor.
|
|
*/
|
|
template<typename Functor, typename Decrypter>
|
|
class DecrypterProxy {
|
|
public:
|
|
/**
|
|
* The type of the functor object.
|
|
*/
|
|
typedef Functor functor_type;
|
|
|
|
/**
|
|
* The type of the decrypter object.
|
|
*/
|
|
typedef Decrypter decrypter_type;
|
|
|
|
/**
|
|
* \brief Constructs an object from a functor and a decrypter.
|
|
* \param func The functor to be used to forward decrypted
|
|
* packets.
|
|
* \param decrypter The decrypter which will be used to decrypt
|
|
* packets
|
|
*/
|
|
DecrypterProxy(const functor_type& func,
|
|
const decrypter_type& decr = decrypter_type());
|
|
|
|
/**
|
|
* \brief Retrieves a reference to the decrypter object.
|
|
*/
|
|
decrypter_type& decrypter();
|
|
|
|
/**
|
|
* \brief Retrieves a const reference to the decrypter object.
|
|
*/
|
|
const decrypter_type& decrypter() const;
|
|
|
|
/**
|
|
* \brief The operator() which decrypts packets and forwards
|
|
* them to the functor.
|
|
*/
|
|
bool operator() (PDU& pdu);
|
|
private:
|
|
Functor functor_;
|
|
decrypter_type decrypter_;
|
|
};
|
|
|
|
/**
|
|
* \brief Wrapper function to create a DecrypterProxy using a
|
|
* WEPDecrypter as the Decrypter template parameter.
|
|
*
|
|
* \param functor The functor to be forwarded to the DecrypterProxy
|
|
* constructor.
|
|
*/
|
|
template<typename Functor>
|
|
DecrypterProxy<Functor, WEPDecrypter> make_wep_decrypter_proxy(const Functor& functor);
|
|
|
|
#ifdef TINS_HAVE_WPA2_DECRYPTION
|
|
/**
|
|
* \brief Wrapper function to create a DecrypterProxy using a
|
|
* WPA2Decrypter as the Decrypter template parameter.
|
|
*
|
|
* \param functor The functor to be forwarded to the DecrypterProxy
|
|
* constructor.
|
|
*/
|
|
template<typename Functor>
|
|
DecrypterProxy<Functor, WPA2Decrypter> make_wpa2_decrypter_proxy(const Functor& functor) {
|
|
return DecrypterProxy<Functor, WPA2Decrypter>(functor);
|
|
}
|
|
#endif // TINS_HAVE_WPA2_DECRYPTION
|
|
|
|
// Implementation section
|
|
|
|
// DecrypterProxy
|
|
|
|
template<typename Functor, typename Decrypter>
|
|
DecrypterProxy<Functor, Decrypter>::DecrypterProxy(const functor_type& func,
|
|
const decrypter_type& decr)
|
|
: functor_(func), decrypter_(decr) {
|
|
|
|
}
|
|
|
|
template<typename Functor, typename Decrypter>
|
|
typename DecrypterProxy<Functor, Decrypter>::decrypter_type &
|
|
DecrypterProxy<Functor, Decrypter>::decrypter() {
|
|
return decrypter_;
|
|
}
|
|
|
|
template<typename Functor, typename Decrypter>
|
|
const typename DecrypterProxy<Functor, Decrypter>::decrypter_type &
|
|
DecrypterProxy<Functor, Decrypter>::decrypter() const {
|
|
return decrypter_;
|
|
}
|
|
|
|
template<typename Functor, typename Decrypter>
|
|
bool DecrypterProxy<Functor, Decrypter>::operator() (PDU& pdu) {
|
|
return decrypter_.decrypt(pdu) ? functor_(pdu) : true;
|
|
}
|
|
|
|
template<typename Functor>
|
|
DecrypterProxy<Functor, WEPDecrypter> make_wep_decrypter_proxy(const Functor& functor) {
|
|
return DecrypterProxy<Functor, WEPDecrypter>(functor);
|
|
}
|
|
|
|
} // Crypto
|
|
} // Tins
|
|
|
|
#endif // TINS_CRYPTO_H
|