/* * 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 #if !defined(TINS_CRYPTO_H) && defined(TINS_HAVE_DOT11) #define TINS_CRYPTO_H #include #include #include #ifdef TINS_HAVE_WPA2_CALLBACKS #include #endif // TINS_HAVE_WPA2_CALLBACKS #include #include 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 ptk_type; /** * The type used to hold the PMK (this has to be PMK_SIZE bytes long). */ typedef std::vector 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 passwords_type; PDU* decrypt(RawPDU& raw, const std::string& password); passwords_type passwords_; std::vector 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 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 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. * * 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 pmks_map; typedef std::map 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 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 DecrypterProxy 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 DecrypterProxy make_wpa2_decrypter_proxy(const Functor& functor) { return DecrypterProxy(functor); } #endif // TINS_HAVE_WPA2_DECRYPTION // Implementation section // DecrypterProxy template DecrypterProxy::DecrypterProxy(const functor_type& func, const decrypter_type& decr) : functor_(func), decrypter_(decr) { } template typename DecrypterProxy::decrypter_type & DecrypterProxy::decrypter() { return decrypter_; } template const typename DecrypterProxy::decrypter_type & DecrypterProxy::decrypter() const { return decrypter_; } template bool DecrypterProxy::operator() (PDU& pdu) { return decrypter_.decrypt(pdu) ? functor_(pdu) : true; } template DecrypterProxy make_wep_decrypter_proxy(const Functor& functor) { return DecrypterProxy(functor); } } // Crypto } // Tins #endif // TINS_CRYPTO_H