diff --git a/Common++/header/Logger.h b/Common++/header/Logger.h index 6973cf530d..fafe6a9e37 100644 --- a/Common++/header/Logger.h +++ b/Common++/header/Logger.h @@ -103,6 +103,7 @@ namespace pcpp PacketLogModuleSomeIpSdLayer, ///< SomeIpSdLayer module (Packet++) PacketLogModuleWakeOnLanLayer, ///< WakeOnLanLayer module (Packet++) PacketLogModuleSmtpLayer, ///< SmtpLayer module (Packet++) + PacketLogModuleWireGuardLayer, ///< WireGuardLayer module (Packet++) PcapLogModuleWinPcapLiveDevice, ///< WinPcapLiveDevice module (Pcap++) PcapLogModuleRemoteDevice, ///< WinPcapRemoteDevice module (Pcap++) PcapLogModuleLiveDevice, ///< PcapLiveDevice module (Pcap++) diff --git a/Packet++/CMakeLists.txt b/Packet++/CMakeLists.txt index cc7a8268d2..bab9488be6 100644 --- a/Packet++/CMakeLists.txt +++ b/Packet++/CMakeLists.txt @@ -64,6 +64,7 @@ add_library( src/VrrpLayer.cpp src/VxlanLayer.cpp src/WakeOnLanLayer.cpp + src/WireGuardLayer.cpp # Force hash-library pcapng to be link fully static ) @@ -133,7 +134,8 @@ set(public_headers header/VlanLayer.h header/VrrpLayer.h header/VxlanLayer.h - header/WakeOnLanLayer.h) + header/WakeOnLanLayer.h + header/WireGuardLayer.h) # Don't use set_target_properties CMake limit to 50 elements set_property(TARGET Packet++ PROPERTY PUBLIC_HEADER ${public_headers}) diff --git a/Packet++/header/ProtocolType.h b/Packet++/header/ProtocolType.h index ada6950c5e..bb775fb9ff 100644 --- a/Packet++/header/ProtocolType.h +++ b/Packet++/header/ProtocolType.h @@ -347,6 +347,11 @@ namespace pcpp */ const ProtocolType LDAP = 55; + /* + * WireGuard protocol + */ + const ProtocolType WireGuard = 56; + /** * An enum representing OSI model layers */ diff --git a/Packet++/header/WireGuardLayer.h b/Packet++/header/WireGuardLayer.h new file mode 100644 index 0000000000..5d5a9dca24 --- /dev/null +++ b/Packet++/header/WireGuardLayer.h @@ -0,0 +1,510 @@ +#pragma once + +#include "Layer.h" +#include "IpAddress.h" +#include "MacAddress.h" + +/// @file + +/** + * \namespace pcpp + * \brief The main namespace for the PcapPlusPlus lib + */ +namespace pcpp +{ + + /** + * @class WireGuardLayer + * Represents a WireGuard protocol layer + */ + class WireGuardLayer : public Layer + { + protected: +#pragma pack(push, 1) + /** + * @struct wg_common_header + * Represents the common header for all WireGuard message types + */ + struct wg_common_header + { + /** Message type field */ + uint8_t messageType; + /** Reserved field (3 bytes) */ + uint8_t reserved[3]; + }; +#pragma pack(pop) + wg_common_header* getBasicHeader() const + { + return reinterpret_cast(m_Data); + } + + WireGuardLayer() = default; + + public: + /** + * WireGuard message types + */ + enum class WireGuardMessageType + { + /** Unknown Initiation message */ + Unknown = 0, + /** Handshake Initiation message */ + HandshakeInitiation = 1, + /** Handshake Response message */ + HandshakeResponse = 2, + /** Cookie Reply message */ + CookieReply = 3, + /** Transport Data message */ + TransportData = 4 + }; + + /** + * Constructs a WireGuardLayer object. + * + * @param data Pointer to the raw data representing the WireGuard layer + * @param dataLen Length of the data + * @param prevLayer Pointer to the previous layer in the packet (if any) + * @param packet Pointer to the packet this layer belongs to + */ + WireGuardLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : Layer(data, dataLen, prevLayer, packet, WireGuard) + {} + + /** + * Checks if the given port numbers are WireGuard ports. + * + * @param portSrc The source port number to check + * @param portDst The destination port number to check + * @return True if either port matches the WireGuard port (51820), false otherwise + */ + static bool isWireGuardPorts(uint16_t portSrc, uint16_t portDst) + { + return (portSrc == 51820 || portDst == 51820); + } + + /** + * Checks if the given data represents a WireGuard message. + * + * @param data Pointer to the raw data + * @param dataLen Length of the data + * @return True if the data starts with a valid WireGuard message type, false otherwise + */ + static bool isDataValid(const uint8_t* data, size_t dataLen); + + /** + * Parses the raw data into a WireGuard layer. + * + * @param data Pointer to the raw data + * @param dataLen Length of the data + * @param prevLayer Pointer to the previous layer + * @param packet Pointer to the packet + * @return A pointer to the parsed WireGuardLayer, or nullptr if parsing fails + */ + static WireGuardLayer* parseWireGuardLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); + + /** + * @return String representation of the message type. + */ + std::string getMessageTypeAsString() const; + + /** + * @return The message type as an unsigned 32-bit integer. + */ + uint8_t getMessageType() const; + + /** + * @return The reserved field as a 32-bit integer. + */ + uint32_t getReserved() const; + + /** + * Does nothing for this layer (WireGuard layer is always last) + */ + void parseNextLayer() override + {} + + /** + * @return Size of the header in bytes. + */ + size_t getHeaderLen() const override; + + /** + * No fields to compute or update, so this method is left empty. + */ + void computeCalculateFields() override + {} + + /** + * Converts the WireGuard layer to a string representation. + * + * @return String representation of the WireGuard layer + */ + std::string toString() const override; + + /** + * @return OSI model layer corresponding to the Network layer + */ + OsiModelLayer getOsiModelLayer() const override + { + return OsiModelNetworkLayer; + } + + /** + * @return The message type as a WireGuardMessageType enum value. + */ + virtual WireGuardMessageType getWireGuardMessageType() const + { + return WireGuardMessageType::Unknown; + } + }; + + /** + * @class WireGuardHandshakeInitiationLayer + * Represents the Handshake Initiation message layer + */ + class WireGuardHandshakeInitiationLayer : public WireGuardLayer + { + private: +#pragma pack(push, 1) + /** + * @struct wg_handshake_initiation + * Represents the Handshake Initiation message structure + */ + typedef struct wg_handshake_initiation : wg_common_header + { + /** Sender index */ + uint32_t senderIndex; + /** Initiator's ephemeral public key */ + uint8_t initiatorEphemeral[32]; + /** Encrypted initiator's static key */ + uint8_t encryptedInitiatorStatic[48]; + /** Encrypted timestamp */ + uint8_t encryptedTimestamp[28]; + /** MAC1 field */ + uint8_t mac1[16]; + /** MAC2 field */ + uint8_t mac2[16]; + } wg_handshake_initiation; +#pragma pack(pop) + wg_handshake_initiation* getHandshakeInitiationHeader() const + { + return reinterpret_cast(getBasicHeader()); + } + + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + WireGuardHandshakeInitiationLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : WireGuardLayer(data, dataLen, prevLayer, packet) + {} + + /** + * A constructor that creates a new Handshake Initiation message + * @param[in] senderIndex The sender's index + * @param[in] initiatorEphemeral The initiator's ephemeral public key + * @param[in] encryptedInitiatorStatic The encrypted initiator's static key + * @param[in] encryptedTimestamp The encrypted timestamp + * @param[in] mac1 The MAC1 field + * @param[in] mac2 The MAC2 field + */ + WireGuardHandshakeInitiationLayer(uint32_t senderIndex, const uint8_t initiatorEphemeral[32], + const uint8_t encryptedInitiatorStatic[48], + const uint8_t encryptedTimestamp[28], const uint8_t mac1[16], + const uint8_t mac2[16]); + + /** + * @return The sender index as a 32-bit integer. + */ + uint32_t getSenderIndex() const; + + /** + * @return An array containing the initiator's ephemeral public key. + */ + std::array getInitiatorEphemeral() const; + + /** + * @return An array containing the encrypted initiator's static key. + */ + std::array getEncryptedInitiatorStatic() const; + + /** + * @return An array containing the encrypted timestamp. + */ + std::array getEncryptedTimestamp() const; + + /** + * @return An array containing the MAC1 field. + */ + std::array getMac1() const; + + /** + * @return An array containing the MAC2 field. + */ + std::array getMac2() const; + + // implement abstract methods + + /** + * @return WireGuardMessageType enum value indicating HandshakeInitiation. + */ + WireGuardMessageType getWireGuardMessageType() const override + { + return WireGuardMessageType::HandshakeInitiation; + } + }; + + /** + * @class WireGuardHandshakeResponseLayer + * Represents a Handshake Response message + */ + class WireGuardHandshakeResponseLayer : public WireGuardLayer + { + private: +#pragma pack(push, 1) + /** + * @struct wg_handshake_response + * Represents the Handshake Response message structure + */ + typedef struct wg_handshake_response : wg_common_header + { + /** Sender index */ + uint32_t senderIndex; + /** Receiver index */ + uint32_t receiverIndex; + /** Responder's ephemeral public key */ + uint8_t responderEphemeral[32]; + /** Encrypted empty field */ + uint8_t encryptedEmpty[16]; + /** MAC1 field */ + uint8_t mac1[16]; + /** MAC2 field */ + uint8_t mac2[16]; + } wg_handshake_response; +#pragma pack(pop) + + wg_handshake_response* getHandshakeResponseHeader() const + { + return reinterpret_cast(getBasicHeader()); + } + + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + WireGuardHandshakeResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : WireGuardLayer(data, dataLen, prevLayer, packet) + {} + + /** + * A constructor that creates a new Handshake Response message + * @param[in] senderIndex The sender index + * @param[in] receiverIndex The receiver index + * @param[in] responderEphemeral The responder's ephemeral public key + * @param[in] encryptedEmpty The encrypted empty field + * @param[in] mac1 The MAC1 field + * @param[in] mac2 The MAC2 field + */ + WireGuardHandshakeResponseLayer(uint32_t senderIndex, uint32_t receiverIndex, + const uint8_t responderEphemeral[32], const uint8_t encryptedEmpty[16], + const uint8_t mac1[16], const uint8_t mac2[16]); + + /** + * @return The sender index as a 32-bit unsigned integer. + */ + uint32_t getSenderIndex() const; + + /** + * @return The receiver index as a 32-bit unsigned integer. + */ + uint32_t getReceiverIndex() const; + + /** + * @return The responder's ephemeral public key as an array of 32 bytes. + */ + std::array getResponderEphemeral() const; + + /** + * @return The encrypted empty field as an array of 16 bytes. + */ + std::array getEncryptedEmpty() const; + + /** + * @return The MAC1 field as an array of 16 bytes. + */ + std::array getMac1() const; + + /** + * @return The MAC2 field as an array of 16 bytes. + */ + std::array getMac2() const; + + // implement abstract methods + + /** + * @return The message type as a WireGuardMessageType enum value. + */ + WireGuardMessageType getWireGuardMessageType() const override + { + return WireGuardMessageType::HandshakeResponse; + } + }; + + /** + * @class WireGuardCookieReplyLayer + * Represents a Cookie Reply message + */ + class WireGuardCookieReplyLayer : public WireGuardLayer + { + private: +#pragma pack(push, 1) + /** + * @struct wg_cookie_reply + * Represents the Cookie Reply message structure + */ + typedef struct wg_cookie_reply : wg_common_header + { + /** Receiver index */ + uint32_t receiverIndex; + /** Nonce field */ + uint8_t nonce[24]; + /** Encrypted cookie */ + uint8_t encryptedCookie[32]; + } wg_cookie_reply; +#pragma pack(pop) + + wg_cookie_reply* getCookieReplyHeader() const + { + return reinterpret_cast(getBasicHeader()); + } + + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + WireGuardCookieReplyLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : WireGuardLayer(data, dataLen, prevLayer, packet) + {} + + /** + * A constructor that creates a new Cookie Reply message + * @param[in] receiverIndex The receiver index + * @param[in] nonce The nonce field + * @param[in] encryptedCookie The encrypted cookie + */ + WireGuardCookieReplyLayer(uint32_t receiverIndex, const uint8_t nonce[24], const uint8_t encryptedCookie[32]); + + /** + * @return The receiver index as a 32-bit unsigned integer. + */ + uint32_t getReceiverIndex() const; + + /** + * @return The nonce field as an array of 24 bytes. + */ + std::array getNonce() const; + + /** + * @return The encrypted cookie as an array of 32 bytes. + */ + std::array getEncryptedCookie() const; + + // implement abstract methods + + /** + * @return The message type as a WireGuardMessageType enum value. + */ + WireGuardMessageType getWireGuardMessageType() const override + { + return WireGuardMessageType::CookieReply; + } + }; + + /** + * @class WireGuardTransportDataLayer + * Represents a Transport Data message + */ + class WireGuardTransportDataLayer : public WireGuardLayer + { + private: +#pragma pack(push, 1) + /** + * @struct wg_transport_data + * Represents the Transport Data message structure + */ + typedef struct wg_transport_data : wg_common_header + { + /** Receiver index */ + uint32_t receiverIndex; + /** Counter field */ + uint64_t counter; + /** Flexible array member for encrypted data */ + uint8_t encryptedData[0]; + } wg_transport_data; +#pragma pack(pop) + + wg_transport_data* getTransportHeader() const + { + return reinterpret_cast(getBasicHeader()); + } + + public: + /** + * A constructor that creates the layer from an existing packet raw data + * @param[in] data A pointer to the raw data + * @param[in] dataLen Size of the data in bytes + * @param[in] prevLayer A pointer to the previous layer + * @param[in] packet A pointer to the Packet instance where layer will be stored in + */ + WireGuardTransportDataLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + : WireGuardLayer(data, dataLen, prevLayer, packet) + {} + + /** + * A constructor that creates a new Transport Data message + * @param[in] receiverIndex The receiver index + * @param[in] counter The counter field + * @param[in] encryptedData The encrypted data + * @param[in] encryptedDataLen The length of the encrypted data + */ + WireGuardTransportDataLayer(uint32_t receiverIndex, uint64_t counter, const uint8_t* encryptedData, + size_t encryptedDataLen); + + /** + * @return The receiver index as a 32-bit unsigned integer. + */ + uint32_t getReceiverIndex() const; + + /** + * @return The counter field as a 64-bit unsigned integer. + */ + uint64_t getCounter() const; + + /** + * @return A pointer to the encrypted data field. + */ + const uint8_t* getEncryptedData() const; + + // implement abstract methods + + /** + * @return The message type as a WireGuardMessageType enum value. + */ + WireGuardMessageType getWireGuardMessageType() const override + { + return WireGuardMessageType::TransportData; + } + }; +} // namespace pcpp diff --git a/Packet++/src/UdpLayer.cpp b/Packet++/src/UdpLayer.cpp index 2e17b9cddc..274695b856 100644 --- a/Packet++/src/UdpLayer.cpp +++ b/Packet++/src/UdpLayer.cpp @@ -15,6 +15,7 @@ #include "NtpLayer.h" #include "SomeIpLayer.h" #include "WakeOnLanLayer.h" +#include "WireGuardLayer.h" #include "PacketUtils.h" #include "Logger.h" #include @@ -134,6 +135,13 @@ namespace pcpp m_NextLayer = SomeIpLayer::parseSomeIpLayer(udpData, udpDataLen, this, m_Packet); else if ((WakeOnLanLayer::isWakeOnLanPort(portDst) && WakeOnLanLayer::isDataValid(udpData, udpDataLen))) m_NextLayer = new WakeOnLanLayer(udpData, udpDataLen, this, m_Packet); + else if ((WireGuardLayer::isWireGuardPorts(portDst, portSrc) && + WireGuardLayer::isDataValid(udpData, udpDataLen))) + { + m_NextLayer = WireGuardLayer::parseWireGuardLayer(udpData, udpDataLen, this, m_Packet); + if (!m_NextLayer) + m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); + } else m_NextLayer = new PayloadLayer(udpData, udpDataLen, this, m_Packet); } diff --git a/Packet++/src/WireGuardLayer.cpp b/Packet++/src/WireGuardLayer.cpp new file mode 100644 index 0000000000..2f88b6c7ab --- /dev/null +++ b/Packet++/src/WireGuardLayer.cpp @@ -0,0 +1,296 @@ +#define LOG_MODULE PacketLogModuleWireGuardLayer + +#include "UdpLayer.h" +#include "WireGuardLayer.h" +#include "EndianPortable.h" +#include +#include + +namespace pcpp +{ + WireGuardLayer* WireGuardLayer::parseWireGuardLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) + { + if (dataLen < sizeof(WireGuardLayer::wg_common_header)) + return nullptr; + wg_common_header* wgHeader = reinterpret_cast(data); + + switch (wgHeader->messageType) + { + case static_cast(WireGuardMessageType::HandshakeInitiation): + return new WireGuardHandshakeInitiationLayer(data, dataLen, prevLayer, packet); + case static_cast(WireGuardMessageType::HandshakeResponse): + return new WireGuardHandshakeResponseLayer(data, dataLen, prevLayer, packet); + case static_cast(WireGuardMessageType::CookieReply): + return new WireGuardCookieReplyLayer(data, dataLen, prevLayer, packet); + case static_cast(WireGuardMessageType::TransportData): + return new WireGuardTransportDataLayer(data, dataLen, prevLayer, packet); + default: + return nullptr; + } + } + + std::string WireGuardLayer::getMessageTypeAsString() const + { + uint32_t messageType = getMessageType(); + switch (messageType) + { + case static_cast(WireGuardMessageType::HandshakeInitiation): + return "Handshake Initiation"; + case static_cast(WireGuardMessageType::HandshakeResponse): + return "Handshake Response"; + case static_cast(WireGuardMessageType::CookieReply): + return "Cookie Reply"; + case static_cast(WireGuardMessageType::TransportData): + return "Transport Data"; + default: + return "Unknown"; + } + } + + std::string WireGuardLayer::toString() const + { + return "WireGuard Layer, " + getMessageTypeAsString() + " message"; + } + + size_t WireGuardLayer::getHeaderLen() const + { + return m_DataLen; + } + + uint8_t WireGuardLayer::getMessageType() const + { + return getBasicHeader()->messageType; + } + + uint32_t WireGuardLayer::getReserved() const + { + uint32_t reservedValue = 0; + memcpy(&reservedValue, getBasicHeader()->reserved, 3); + return be32toh(reservedValue); + } + + bool WireGuardLayer::isDataValid(const uint8_t* data, size_t dataLen) + { + if (dataLen < sizeof(WireGuardLayer::wg_common_header)) + return false; + + uint8_t messageType = data[0]; + return messageType >= static_cast(WireGuardLayer::WireGuardMessageType::HandshakeInitiation) && + messageType <= static_cast(WireGuardLayer::WireGuardMessageType::TransportData); + } + + // ~~~~~~~~~~~~~~~~~~~~ + // WireGuardHandshakeInitiationLayer + // ~~~~~~~~~~~~~~~~~~~~ + + WireGuardHandshakeInitiationLayer::WireGuardHandshakeInitiationLayer(uint32_t senderIndex, + const uint8_t initiatorEphemeral[32], + const uint8_t encryptedInitiatorStatic[48], + const uint8_t encryptedTimestamp[28], + const uint8_t mac1[16], const uint8_t mac2[16]) + { + const size_t messageLen = sizeof(wg_handshake_initiation); + m_DataLen = messageLen; + m_Data = new uint8_t[messageLen]; + memset(m_Data, 0, messageLen); + + wg_handshake_initiation* msg = reinterpret_cast(m_Data); + + msg->messageType = static_cast(WireGuardMessageType::HandshakeInitiation); + memset(msg->reserved, 0, 3); + msg->senderIndex = htobe32(senderIndex); + + memcpy(msg->initiatorEphemeral, initiatorEphemeral, 32); + memcpy(msg->encryptedInitiatorStatic, encryptedInitiatorStatic, 48); + memcpy(msg->encryptedTimestamp, encryptedTimestamp, 28); + memcpy(msg->mac1, mac1, 16); + memcpy(msg->mac2, mac2, 16); + + m_Protocol = WireGuard; + } + + uint32_t WireGuardHandshakeInitiationLayer::getSenderIndex() const + { + return be32toh(getHandshakeInitiationHeader()->senderIndex); + } + + std::array WireGuardHandshakeInitiationLayer::getInitiatorEphemeral() const + { + std::array ephemeralArray; + memcpy(ephemeralArray.data(), getHandshakeInitiationHeader()->initiatorEphemeral, 32); + return ephemeralArray; + } + + std::array WireGuardHandshakeInitiationLayer::getEncryptedInitiatorStatic() const + { + std::array initArray; + memcpy(initArray.data(), getHandshakeInitiationHeader()->encryptedInitiatorStatic, 48); + return initArray; + } + + std::array WireGuardHandshakeInitiationLayer::getEncryptedTimestamp() const + { + std::array tsArray; + memcpy(tsArray.data(), getHandshakeInitiationHeader()->encryptedTimestamp, 28); + return tsArray; + } + + std::array WireGuardHandshakeInitiationLayer::getMac1() const + { + std::array mac1Array; + memcpy(mac1Array.data(), getHandshakeInitiationHeader()->mac1, 16); + return mac1Array; + } + + std::array WireGuardHandshakeInitiationLayer::getMac2() const + { + std::array mac2Array; + memcpy(mac2Array.data(), getHandshakeInitiationHeader()->mac2, 16); + return mac2Array; + } + + // ~~~~~~~~~~~~~~~~~~~~ + // WireGuardHandshakeResponseLayer + // ~~~~~~~~~~~~~~~~~~~~ + + WireGuardHandshakeResponseLayer::WireGuardHandshakeResponseLayer(uint32_t senderIndex, uint32_t receiverIndex, + const uint8_t responderEphemeral[32], + const uint8_t encryptedEmpty[16], + const uint8_t mac1[16], const uint8_t mac2[16]) + { + const size_t messageLen = sizeof(wg_handshake_response); + m_DataLen = messageLen; + m_Data = new uint8_t[messageLen]; + wg_handshake_response* msg = reinterpret_cast(m_Data); + + msg->messageType = static_cast(WireGuardMessageType::HandshakeResponse); + memset(msg->reserved, 0, 3); + msg->senderIndex = htobe32(senderIndex); + msg->receiverIndex = htobe32(receiverIndex); + memcpy(msg->responderEphemeral, responderEphemeral, 32); + memcpy(msg->encryptedEmpty, encryptedEmpty, 16); + memcpy(msg->mac1, mac1, 16); + memcpy(msg->mac2, mac2, 16); + + m_Protocol = WireGuard; + } + + uint32_t WireGuardHandshakeResponseLayer::getSenderIndex() const + { + return be32toh(getHandshakeResponseHeader()->senderIndex); + } + + uint32_t WireGuardHandshakeResponseLayer::getReceiverIndex() const + { + return be32toh(getHandshakeResponseHeader()->receiverIndex); + } + + std::array WireGuardHandshakeResponseLayer::getResponderEphemeral() const + { + std::array responderEphemeralArray; + memcpy(responderEphemeralArray.data(), getHandshakeResponseHeader()->responderEphemeral, 32); + return responderEphemeralArray; + } + + std::array WireGuardHandshakeResponseLayer::getEncryptedEmpty() const + { + std::array encryptedEmptyArray; + memcpy(encryptedEmptyArray.data(), getHandshakeResponseHeader()->encryptedEmpty, 16); + return encryptedEmptyArray; + } + + std::array WireGuardHandshakeResponseLayer::getMac1() const + { + std::array mac1Array; + memcpy(mac1Array.data(), getHandshakeResponseHeader()->mac1, 16); + return mac1Array; + } + + std::array WireGuardHandshakeResponseLayer::getMac2() const + { + std::array mac2Array; + memcpy(mac2Array.data(), getHandshakeResponseHeader()->mac2, 16); + return mac2Array; + } + + // ~~~~~~~~~~~~~~~~~~~~ + // WireGuardCookieReplyLayer + // ~~~~~~~~~~~~~~~~~~~~ + + WireGuardCookieReplyLayer::WireGuardCookieReplyLayer(uint32_t receiverIndex, const uint8_t nonce[24], + const uint8_t encryptedCookie[32]) + { + const size_t messageLen = sizeof(wg_cookie_reply); + m_DataLen = messageLen; + m_Data = new uint8_t[messageLen]; + memset(m_Data, 0, messageLen); + + wg_cookie_reply* msg = reinterpret_cast(m_Data); + + msg->messageType = static_cast(WireGuardMessageType::CookieReply); + memset(msg->reserved, 0, 3); + msg->receiverIndex = htobe32(receiverIndex); + memcpy(msg->nonce, nonce, 24); + memcpy(msg->encryptedCookie, encryptedCookie, 32); + + m_Protocol = WireGuard; + } + + uint32_t WireGuardCookieReplyLayer::getReceiverIndex() const + { + return be32toh(getCookieReplyHeader()->receiverIndex); + } + + std::array WireGuardCookieReplyLayer::getNonce() const + { + std::array nonceArray; + memcpy(nonceArray.data(), getCookieReplyHeader()->nonce, 24); + return nonceArray; + } + + std::array WireGuardCookieReplyLayer::getEncryptedCookie() const + { + std::array encryptedCookieArray; + memcpy(encryptedCookieArray.data(), getCookieReplyHeader()->encryptedCookie, 32); + return encryptedCookieArray; + } + + // ~~~~~~~~~~~~~~~~~~~~ + // WireGuardTransportDataLayer + // ~~~~~~~~~~~~~~~~~~~~ + + WireGuardTransportDataLayer::WireGuardTransportDataLayer(uint32_t receiverIndex, uint64_t counter, + const uint8_t* encryptedData, size_t encryptedDataLen) + { + const size_t messageLen = sizeof(wg_transport_data) + encryptedDataLen; + m_DataLen = messageLen; + m_Data = new uint8_t[messageLen]; + memset(m_Data, 0, messageLen); + + wg_transport_data* msg = reinterpret_cast(m_Data); + + msg->messageType = static_cast(WireGuardMessageType::TransportData); + memset(msg->reserved, 0, 3); + msg->receiverIndex = htobe32(receiverIndex); + msg->counter = htobe64(counter); + memcpy(m_Data + sizeof(wg_transport_data), encryptedData, encryptedDataLen); + + m_Protocol = WireGuard; + } + + uint32_t WireGuardTransportDataLayer::getReceiverIndex() const + { + return be32toh(getTransportHeader()->receiverIndex); + } + + uint64_t WireGuardTransportDataLayer::getCounter() const + { + return be64toh(getTransportHeader()->counter); + } + + const uint8_t* WireGuardTransportDataLayer::getEncryptedData() const + { + return getTransportHeader()->encryptedData; + } + +} // namespace pcpp diff --git a/README.md b/README.md index 385c30db5d..d93c647ed6 100644 --- a/README.md +++ b/README.md @@ -242,43 +242,44 @@ PcapPlusPlus currently supports parsing, editing and creation of packets of the 22. NDP 23. Raw IP (IPv4 & IPv6) 24. VRRP (IPv4 & IPv6) +25. WireGuard ### Transport Layer (L4) -25. COTP -26. GTP (v1) -27. IPSec AH & ESP - parsing only (no editing capabilities) -28. TCP -29. TPKT -30. UDP +26. COTP +27. GTP (v1) +28. IPSec AH & ESP - parsing only (no editing capabilities) +29. TCP +30. TPKT +31. UDP ### Session Layer (L5) -31. SDP -32. SIP +32. SDP +33. SIP ### Presentation Layer (L6) -33. SSL/TLS - parsing only (no editing capabilities) +34. SSL/TLS - parsing only (no editing capabilities) ### Application Layer (L7) -34. ASN.1 decoder and encoder -35. BGP (v4) -36. DHCP -37. DHCPv6 -38. DNS -39. FTP -40. HTTP headers (request & response) -41. LDAP -42. NTP (v3, v4) -43. Radius -44. S7 Communication (S7comm) -45. SMTP -46. SOME/IP -47. SSH - parsing only (no editing capabilities) -48. Telnet - parsing only (no editing capabilities) -49. Generic payload +35. ASN.1 decoder and encoder +36. BGP (v4) +37. DHCP +38. DHCPv6 +39. DNS +40. FTP +41. HTTP headers (request & response) +42. LDAP +43. NTP (v3, v4) +44. Radius +45. S7 Communication (S7comm) +46. SMTP +47. SOME/IP +48. SSH - parsing only (no editing capabilities) +49. Telnet - parsing only (no editing capabilities) +50. Generic payload ## DPDK And PF_RING Support diff --git a/Tests/Packet++Test/CMakeLists.txt b/Tests/Packet++Test/CMakeLists.txt index 0e6bbc75bc..0bcac69e63 100644 --- a/Tests/Packet++Test/CMakeLists.txt +++ b/Tests/Packet++Test/CMakeLists.txt @@ -42,6 +42,7 @@ add_executable( Tests/VlanMplsTests.cpp Tests/VrrpTest.cpp Tests/WakeOnLanTests.cpp + Tests/WireGuardTests.cpp Utils/TestUtils.cpp) target_link_libraries( diff --git a/Tests/Packet++Test/PacketExamples/WireGuard.pcap b/Tests/Packet++Test/PacketExamples/WireGuard.pcap new file mode 100644 index 0000000000..5d21e849cf Binary files /dev/null and b/Tests/Packet++Test/PacketExamples/WireGuard.pcap differ diff --git a/Tests/Packet++Test/PacketExamples/WireGuardCookieReply.dat b/Tests/Packet++Test/PacketExamples/WireGuardCookieReply.dat new file mode 100644 index 0000000000..cf8caaf01e --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/WireGuardCookieReply.dat @@ -0,0 +1 @@ +aabbccddeeff00504311223308004500005c07d000004011a717c0a801010a000001ca6cca6c0048f55503000000ab7df4060100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/WireGuardHandshakeInitiation.dat b/Tests/Packet++Test/PacketExamples/WireGuardHandshakeInitiation.dat new file mode 100644 index 0000000000..5c3cc26711 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/WireGuardHandshakeInitiation.dat @@ -0,0 +1 @@ +a2e63494b5833a36e5bf5af80800458800b0024b0000401163560a0900010a090002a9c6ca6c009c14c201000000d837d0305fcec7c8e5c8e2e3f7989eef60c228d82329d602b6b1e2bb9d068f89cf9d4d4532780f6d27264f7b98701fdc27a4ec00aeb6becdbef2332f1b4084cadb93823935c012ae255e7b25eff13940c321fa6bd66a2a87b061db1430173e937f569349de2856dc5f2616763eeeafc0533b01dd965e7ec76976e28f683d671200000000000000000000000000000000 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/WireGuardHandshakeResponse.dat b/Tests/Packet++Test/PacketExamples/WireGuardHandshakeResponse.dat new file mode 100644 index 0000000000..8242113990 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/WireGuardHandshakeResponse.dat @@ -0,0 +1 @@ +3a36e5bf5af8a2e63494b5830800458800782a39000040113ba00a0900020a090001ca6ca9c60064148a0200000006f47dabd837d030b18d5550bd4042a37a46823ac08db1ec66839bc0ca2d64bc15cd80232b66232faec24af8918de1060ff5c98e865d5f35f272214c5260110dc4c61e32cdd8542100000000000000000000000000000000 \ No newline at end of file diff --git a/Tests/Packet++Test/PacketExamples/WireGuardTransportData.dat b/Tests/Packet++Test/PacketExamples/WireGuardTransportData.dat new file mode 100644 index 0000000000..766c173f54 --- /dev/null +++ b/Tests/Packet++Test/PacketExamples/WireGuardTransportData.dat @@ -0,0 +1 @@ +a2e63494b5833a36e5bf5af808004500009c024c0000401163f10a0900010a090002a9c6ca6c008814ae0400000006f47dab0000000000000000a4ebc12ee3f990da18033a0789c04e2700f6f5c271d42ac4b4d6262e666549b445a7436e829bffb6ac65f05648bc0c391fe7c5884874376127164940188f03dba67af8388eaab76c593628bf9dc7be03346d912e916dad862545454701364f2d2486d7ced4c8642ce547ddb26ef6a46b \ No newline at end of file diff --git a/Tests/Packet++Test/TestDefinition.h b/Tests/Packet++Test/TestDefinition.h index 32be87090a..477e956671 100644 --- a/Tests/Packet++Test/TestDefinition.h +++ b/Tests/Packet++Test/TestDefinition.h @@ -263,3 +263,10 @@ PTF_TEST_CASE(Asn1EncodingTest); // Implemented in LdapTests.cpp PTF_TEST_CASE(LdapParsingTest); PTF_TEST_CASE(LdapCreationTest); + +// Implemented in WireGuardTests.cpp +PTF_TEST_CASE(WireGuardHandshakeInitParsingTest); +PTF_TEST_CASE(WireGuardHandshakeRespParsingTest); +PTF_TEST_CASE(WireGuardCookieReplyParsingTest); +PTF_TEST_CASE(WireGuardTransportDataParsingTest); +PTF_TEST_CASE(WireGuardCreationTest); diff --git a/Tests/Packet++Test/Tests/WireGuardTests.cpp b/Tests/Packet++Test/Tests/WireGuardTests.cpp new file mode 100644 index 0000000000..9858b3445c --- /dev/null +++ b/Tests/Packet++Test/Tests/WireGuardTests.cpp @@ -0,0 +1,365 @@ +#include "../TestDefinition.h" +#include "../Utils/TestUtils.h" +#include "Packet.h" +#include "WireGuardLayer.h" +#include "SystemUtils.h" +#include +#include "EndianPortable.h" + +PTF_TEST_CASE(WireGuardHandshakeInitParsingTest) +{ + timeval time; + gettimeofday(&time, nullptr); + + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/WireGuardHandshakeInitiation.dat"); + + pcpp::Packet wgHandShakeInitPacket(&rawPacket1); + + PTF_ASSERT_TRUE(wgHandShakeInitPacket.isPacketOfType(pcpp::WireGuard)); + + auto wgLayer = wgHandShakeInitPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(wgLayer); + + auto wgHandShakeInitLayer = wgHandShakeInitPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(wgHandShakeInitLayer); + + PTF_ASSERT_EQUAL(wgHandShakeInitLayer->getMessageTypeAsString(), "Handshake Initiation"); + + PTF_ASSERT_EQUAL(wgHandShakeInitLayer->getMessageType(), 1); + PTF_ASSERT_EQUAL(wgHandShakeInitLayer->getReserved(), 0); + PTF_ASSERT_EQUAL(wgHandShakeInitLayer->getHeaderLen(), 148); + + PTF_ASSERT_EQUAL(wgHandShakeInitLayer->toString(), + "WireGuard Layer, " + wgHandShakeInitLayer->getMessageTypeAsString() + " message"); + + PTF_ASSERT_TRUE(wgHandShakeInitLayer->getWireGuardMessageType() == + pcpp::WireGuardLayer::WireGuardMessageType::HandshakeInitiation); + PTF_ASSERT_EQUAL(wgHandShakeInitLayer->getSenderIndex(), 0xd837d030); + + std::array expectedPublicKey = { 0x5f, 0xce, 0xc7, 0xc8, 0xe5, 0xc8, 0xe2, 0xe3, 0xf7, 0x98, 0x9e, + 0xef, 0x60, 0xc2, 0x28, 0xd8, 0x23, 0x29, 0xd6, 0x02, 0xb6, 0xb1, + 0xe2, 0xbb, 0x9d, 0x06, 0x8f, 0x89, 0xcf, 0x9d, 0x4d, 0x45 }; + PTF_ASSERT_TRUE(wgHandShakeInitLayer->getInitiatorEphemeral() == expectedPublicKey); + + std::array expectedStaticKey = { 0x32, 0x78, 0x0f, 0x6d, 0x27, 0x26, 0x4f, 0x7b, 0x98, 0x70, + 0x1f, 0xdc, 0x27, 0xa4, 0xec, 0x00, 0xae, 0xb6, 0xbe, 0xcd, + 0xbe, 0xf2, 0x33, 0x2f, 0x1b, 0x40, 0x84, 0xca, 0xdb, 0x93, + 0x82, 0x39, 0x35, 0xc0, 0x12, 0xae, 0x25, 0x5e, 0x7b, 0x25, + 0xef, 0xf1, 0x39, 0x40, 0xc3, 0x21, 0xfa, 0x6b }; + PTF_ASSERT_TRUE(wgHandShakeInitLayer->getEncryptedInitiatorStatic() == expectedStaticKey); + + std::array expectedTimestamp = { 0xd6, 0x6a, 0x2a, 0x87, 0xb0, 0x61, 0xdb, 0x14, 0x30, 0x17, + 0x3e, 0x93, 0x7f, 0x56, 0x93, 0x49, 0xde, 0x28, 0x56, 0xdc, + 0x5f, 0x26, 0x16, 0x76, 0x3e, 0xee, 0xaf, 0xc0 }; + PTF_ASSERT_TRUE(wgHandShakeInitLayer->getEncryptedTimestamp() == expectedTimestamp); + + std::array expectedMac1 = { 0x53, 0x3b, 0x01, 0xdd, 0x96, 0x5e, 0x7e, 0xc7, + 0x69, 0x76, 0xe2, 0x8f, 0x68, 0x3d, 0x67, 0x12 }; + PTF_ASSERT_TRUE(wgHandShakeInitLayer->getMac1() == expectedMac1); + + std::array expectedMac2 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + PTF_ASSERT_TRUE(wgHandShakeInitLayer->getMac2() == expectedMac2); +} + +PTF_TEST_CASE(WireGuardHandshakeRespParsingTest) +{ + timeval time; + gettimeofday(&time, nullptr); + + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/WireGuardHandshakeResponse.dat"); + + pcpp::Packet wgHandShakeResponsePacket(&rawPacket1); + + PTF_ASSERT_TRUE(wgHandShakeResponsePacket.isPacketOfType(pcpp::WireGuard)); + auto wgLayer = wgHandShakeResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(wgLayer); + + auto wgHandShakeResponseLayer = wgHandShakeResponsePacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(wgHandShakeResponseLayer); + + PTF_ASSERT_EQUAL(wgHandShakeResponseLayer->getMessageTypeAsString(), "Handshake Response"); + + PTF_ASSERT_EQUAL(wgHandShakeResponseLayer->getMessageType(), 2); + PTF_ASSERT_EQUAL(wgHandShakeResponseLayer->getReserved(), 0); + PTF_ASSERT_EQUAL(wgHandShakeResponseLayer->getHeaderLen(), 92); + + PTF_ASSERT_EQUAL(wgHandShakeResponseLayer->toString(), + "WireGuard Layer, " + wgHandShakeResponseLayer->getMessageTypeAsString() + " message"); + + PTF_ASSERT_TRUE(wgHandShakeResponseLayer->getWireGuardMessageType() == + pcpp::WireGuardLayer::WireGuardMessageType::HandshakeResponse); + + PTF_ASSERT_EQUAL(wgHandShakeResponseLayer->getSenderIndex(), 0x06f47dab); + PTF_ASSERT_EQUAL(wgHandShakeResponseLayer->getReceiverIndex(), 0xd837d030); + + std::array expectedResponderEphemeral = { 0xb1, 0x8d, 0x55, 0x50, 0xbd, 0x40, 0x42, 0xa3, + 0x7a, 0x46, 0x82, 0x3a, 0xc0, 0x8d, 0xb1, 0xec, + 0x66, 0x83, 0x9b, 0xc0, 0xca, 0x2d, 0x64, 0xbc, + 0x15, 0xcd, 0x80, 0x23, 0x2b, 0x66, 0x23, 0x2f }; + PTF_ASSERT_TRUE(wgHandShakeResponseLayer->getResponderEphemeral() == expectedResponderEphemeral); + + std::array encryptedEmptyData = { 0xae, 0xc2, 0x4a, 0xf8, 0x91, 0x8d, 0xe1, 0x06, + 0x0f, 0xf5, 0xc9, 0x8e, 0x86, 0x5d, 0x5f, 0x35 }; + + PTF_ASSERT_TRUE(wgHandShakeResponseLayer->getEncryptedEmpty() == encryptedEmptyData); + + std::array expectedMac1 = { 0xf2, 0x72, 0x21, 0x4c, 0x52, 0x60, 0x11, 0x0d, + 0xc4, 0xc6, 0x1e, 0x32, 0xcd, 0xd8, 0x54, 0x21 }; + PTF_ASSERT_TRUE(wgHandShakeResponseLayer->getMac1() == expectedMac1); + + std::array expectedMac2 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + PTF_ASSERT_TRUE(wgHandShakeResponseLayer->getMac2() == expectedMac2); +} + +PTF_TEST_CASE(WireGuardCookieReplyParsingTest) +{ + timeval time; + gettimeofday(&time, nullptr); + + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/WireGuardCookieReply.dat"); + + pcpp::Packet wgCookieReplyPacket(&rawPacket1); + + PTF_ASSERT_TRUE(wgCookieReplyPacket.isPacketOfType(pcpp::WireGuard)); + auto wgLayer = wgCookieReplyPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(wgLayer); + + auto wgCookieReplyaLayer = wgCookieReplyPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(wgCookieReplyaLayer); + + PTF_ASSERT_EQUAL(wgCookieReplyaLayer->getMessageTypeAsString(), "Cookie Reply"); + + PTF_ASSERT_EQUAL(wgCookieReplyaLayer->getMessageType(), 3); + PTF_ASSERT_EQUAL(wgCookieReplyaLayer->getReserved(), 0); + PTF_ASSERT_EQUAL(wgCookieReplyaLayer->getHeaderLen(), 64); + + PTF_ASSERT_EQUAL(wgCookieReplyaLayer->toString(), + "WireGuard Layer, " + wgCookieReplyaLayer->getMessageTypeAsString() + " message"); + + PTF_ASSERT_TRUE(wgCookieReplyaLayer->getWireGuardMessageType() == + pcpp::WireGuardLayer::WireGuardMessageType::CookieReply); + + PTF_ASSERT_EQUAL(wgCookieReplyaLayer->getReceiverIndex(), 0xab7df406); + + uint8_t nonce[24] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + PTF_ASSERT_TRUE(std::memcmp(wgCookieReplyaLayer->getNonce().data(), nonce, sizeof(nonce)) == 0); + uint8_t encryptedCookie[32] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + PTF_ASSERT_TRUE( + std::memcmp(wgCookieReplyaLayer->getEncryptedCookie().data(), encryptedCookie, sizeof(encryptedCookie)) == 0); +} + +PTF_TEST_CASE(WireGuardTransportDataParsingTest) +{ + timeval time; + gettimeofday(&time, nullptr); + + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/WireGuardTransportData.dat"); + + pcpp::Packet wgTransportDataPacket(&rawPacket1); + PTF_ASSERT_TRUE(wgTransportDataPacket.isPacketOfType(pcpp::WireGuard)); + auto wgLayer = wgTransportDataPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(wgLayer); + + auto wgTransportDataLayer = wgTransportDataPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(wgTransportDataLayer); + + PTF_ASSERT_EQUAL(wgTransportDataLayer->getMessageTypeAsString(), "Transport Data"); + + PTF_ASSERT_EQUAL(wgTransportDataLayer->getMessageType(), 4); + PTF_ASSERT_EQUAL(wgTransportDataLayer->getReserved(), 0); + PTF_ASSERT_EQUAL(wgTransportDataLayer->getHeaderLen(), 128); + + PTF_ASSERT_EQUAL(wgTransportDataLayer->toString(), + "WireGuard Layer, " + wgTransportDataLayer->getMessageTypeAsString() + " message"); + + PTF_ASSERT_TRUE(wgTransportDataLayer->getWireGuardMessageType() == + pcpp::WireGuardLayer::WireGuardMessageType::TransportData); + + PTF_ASSERT_EQUAL(wgTransportDataLayer->getReceiverIndex(), 0x06f47dab); + + PTF_ASSERT_EQUAL(wgTransportDataLayer->getCounter(), 0); + + uint8_t expectedEncryptedData[112] = { 0xa4, 0xeb, 0xc1, 0x2e, 0xe3, 0xf9, 0x90, 0xda, 0x18, 0x03, 0x3a, 0x07, 0x89, + 0xc0, 0x4e, 0x27, 0x00, 0xf6, 0xf5, 0xc2, 0x71, 0xd4, 0x2a, 0xc4, 0xb4, 0xd6, + 0x26, 0x2e, 0x66, 0x65, 0x49, 0xb4, 0x45, 0xa7, 0x43, 0x6e, 0x82, 0x9b, 0xff, + 0xb6, 0xac, 0x65, 0xf0, 0x56, 0x48, 0xbc, 0x0c, 0x39, 0x1f, 0xe7, 0xc5, 0x88, + 0x48, 0x74, 0x37, 0x61, 0x27, 0x16, 0x49, 0x40, 0x18, 0x8f, 0x03, 0xdb, 0xa6, + 0x7a, 0xf8, 0x38, 0x8e, 0xaa, 0xb7, 0x6c, 0x59, 0x36, 0x28, 0xbf, 0x9d, 0xc7, + 0xbe, 0x03, 0x34, 0x6d, 0x91, 0x2e, 0x91, 0x6d, 0xad, 0x86, 0x25, 0x45, 0x45, + 0x47, 0x01, 0x36, 0x4f, 0x2d, 0x24, 0x86, 0xd7, 0xce, 0xd4, 0xc8, 0x64, 0x2c, + 0xe5, 0x47, 0xdd, 0xb2, 0x6e, 0xf6, 0xa4, 0x6b }; + PTF_ASSERT_BUF_COMPARE(wgTransportDataLayer->getEncryptedData(), expectedEncryptedData, + sizeof(expectedEncryptedData)); +} + +PTF_TEST_CASE(WireGuardCreationTest) +{ + timeval time; + gettimeofday(&time, nullptr); + + READ_FILE_AND_CREATE_PACKET(1, "PacketExamples/WireGuardHandshakeInitiation.dat"); + READ_FILE_AND_CREATE_PACKET(2, "PacketExamples/WireGuardHandshakeResponse.dat"); + READ_FILE_AND_CREATE_PACKET(3, "PacketExamples/WireGuardCookieReply.dat"); + READ_FILE_AND_CREATE_PACKET(4, "PacketExamples/WireGuardTransportData.dat"); + + uint8_t origBuffer[1500]; + + // create WireGuard Handshake Initiation message + memcpy(origBuffer, buffer1, bufferLength1); + + uint8_t expectedPublicKeyInit[32] = { 0x5f, 0xce, 0xc7, 0xc8, 0xe5, 0xc8, 0xe2, 0xe3, 0xf7, 0x98, 0x9e, + 0xef, 0x60, 0xc2, 0x28, 0xd8, 0x23, 0x29, 0xd6, 0x02, 0xb6, 0xb1, + 0xe2, 0xbb, 0x9d, 0x06, 0x8f, 0x89, 0xcf, 0x9d, 0x4d, 0x45 }; + + uint8_t expectedStaticKeyInit[48] = { 0x32, 0x78, 0x0f, 0x6d, 0x27, 0x26, 0x4f, 0x7b, 0x98, 0x70, 0x1f, 0xdc, + 0x27, 0xa4, 0xec, 0x00, 0xae, 0xb6, 0xbe, 0xcd, 0xbe, 0xf2, 0x33, 0x2f, + 0x1b, 0x40, 0x84, 0xca, 0xdb, 0x93, 0x82, 0x39, 0x35, 0xc0, 0x12, 0xae, + 0x25, 0x5e, 0x7b, 0x25, 0xef, 0xf1, 0x39, 0x40, 0xc3, 0x21, 0xfa, 0x6b }; + uint8_t expectedTimestampInit[28] = { 0xd6, 0x6a, 0x2a, 0x87, 0xb0, 0x61, 0xdb, 0x14, 0x30, 0x17, + 0x3e, 0x93, 0x7f, 0x56, 0x93, 0x49, 0xde, 0x28, 0x56, 0xdc, + 0x5f, 0x26, 0x16, 0x76, 0x3e, 0xee, 0xaf, 0xc0 }; + + uint8_t expectedMac1Init[16] = { 0x53, 0x3b, 0x01, 0xdd, 0x96, 0x5e, 0x7e, 0xc7, + 0x69, 0x76, 0xe2, 0x8f, 0x68, 0x3d, 0x67, 0x12 }; + + uint8_t expectedMac2Init[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + pcpp::WireGuardHandshakeInitiationLayer newHandshakeInitMessage(0xd837d030, expectedPublicKeyInit, + expectedStaticKeyInit, expectedTimestampInit, + expectedMac1Init, expectedMac2Init); + pcpp::Packet wgHandshakeInitPacket(&rawPacket1); + auto origHandshakeInitMessage = + dynamic_cast(wgHandshakeInitPacket.detachLayer(pcpp::WireGuard)); + PTF_ASSERT_NOT_NULL(origHandshakeInitMessage); + + PTF_ASSERT_EQUAL(newHandshakeInitMessage.getSenderIndex(), 0xd837d030); + PTF_ASSERT_BUF_COMPARE(newHandshakeInitMessage.getInitiatorEphemeral().data(), expectedPublicKeyInit, 32); + PTF_ASSERT_BUF_COMPARE(newHandshakeInitMessage.getEncryptedInitiatorStatic().data(), expectedStaticKeyInit, 48); + PTF_ASSERT_BUF_COMPARE(newHandshakeInitMessage.getEncryptedTimestamp().data(), expectedTimestampInit, 28); + PTF_ASSERT_BUF_COMPARE(newHandshakeInitMessage.getMac1().data(), expectedMac1Init, 16); + PTF_ASSERT_BUF_COMPARE(newHandshakeInitMessage.getMac2().data(), expectedMac2Init, 16); + + PTF_ASSERT_TRUE(wgHandshakeInitPacket.addLayer(&newHandshakeInitMessage)); + + PTF_ASSERT_EQUAL(wgHandshakeInitPacket.getRawPacket()->getRawDataLen(), bufferLength1); + PTF_ASSERT_EQUAL(newHandshakeInitMessage.getDataLen(), origHandshakeInitMessage->getDataLen()); + + PTF_ASSERT_BUF_COMPARE(newHandshakeInitMessage.getData(), origHandshakeInitMessage->getData(), + origHandshakeInitMessage->getDataLen()); + + delete origHandshakeInitMessage; + + // create WireGuard Handshake Response message + memcpy(origBuffer, buffer2, bufferLength2); + uint8_t expectedResponderEphemeralResp[32] = { 0xb1, 0x8d, 0x55, 0x50, 0xbd, 0x40, 0x42, 0xa3, 0x7a, 0x46, 0x82, + 0x3a, 0xc0, 0x8d, 0xb1, 0xec, 0x66, 0x83, 0x9b, 0xc0, 0xca, 0x2d, + 0x64, 0xbc, 0x15, 0xcd, 0x80, 0x23, 0x2b, 0x66, 0x23, 0x2f }; + + uint8_t encryptedEmptyDataResp[16] = { 0xae, 0xc2, 0x4a, 0xf8, 0x91, 0x8d, 0xe1, 0x06, + 0x0f, 0xf5, 0xc9, 0x8e, 0x86, 0x5d, 0x5f, 0x35 }; + + uint8_t expectedMac1Resp[16] = { 0xf2, 0x72, 0x21, 0x4c, 0x52, 0x60, 0x11, 0x0d, + 0xc4, 0xc6, 0x1e, 0x32, 0xcd, 0xd8, 0x54, 0x21 }; + + uint8_t expectedMac2Resp[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + pcpp::WireGuardHandshakeResponseLayer newHandshakeRespMessage( + 0x06f47dab, 0xd837d030, expectedResponderEphemeralResp, encryptedEmptyDataResp, expectedMac1Resp, + expectedMac2Resp); + pcpp::Packet wgHandshakeRespPacket(&rawPacket2); + pcpp::WireGuardHandshakeResponseLayer* origHandshakeRespMessage = + dynamic_cast(wgHandshakeRespPacket.detachLayer(pcpp::WireGuard)); + PTF_ASSERT_NOT_NULL(origHandshakeRespMessage); + PTF_ASSERT_EQUAL(newHandshakeRespMessage.getDataLen(), origHandshakeRespMessage->getDataLen()); + PTF_ASSERT_EQUAL(newHandshakeRespMessage.getSenderIndex(), 0x06f47dab); + PTF_ASSERT_EQUAL(newHandshakeRespMessage.getReceiverIndex(), 0xd837d030); + PTF_ASSERT_BUF_COMPARE(newHandshakeRespMessage.getResponderEphemeral().data(), expectedResponderEphemeralResp, 32); + PTF_ASSERT_BUF_COMPARE(newHandshakeRespMessage.getEncryptedEmpty().data(), encryptedEmptyDataResp, 16); + PTF_ASSERT_BUF_COMPARE(newHandshakeRespMessage.getMac1().data(), expectedMac1Resp, 16); + PTF_ASSERT_BUF_COMPARE(newHandshakeRespMessage.getMac2().data(), expectedMac2Resp, 16); + + PTF_ASSERT_TRUE(wgHandshakeRespPacket.addLayer(&newHandshakeRespMessage)); + + PTF_ASSERT_EQUAL(wgHandshakeRespPacket.getRawPacket()->getRawDataLen(), bufferLength2); + PTF_ASSERT_EQUAL(newHandshakeRespMessage.getDataLen(), origHandshakeRespMessage->getDataLen()); + + PTF_ASSERT_BUF_COMPARE(newHandshakeRespMessage.getData(), origHandshakeRespMessage->getData(), + origHandshakeRespMessage->getDataLen()); + + delete origHandshakeRespMessage; + + // create WireGuard Cookie Reply message + + memcpy(origBuffer, buffer3, bufferLength3); + + uint8_t nonce[24] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t encryptedCookie[32] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + pcpp::WireGuardCookieReplyLayer newCookieReplyMessage(0xab7df406, nonce, encryptedCookie); + pcpp::Packet wgCookieReplyPacket(&rawPacket3); + + auto origCookieReplyMessage = + dynamic_cast(wgCookieReplyPacket.detachLayer(pcpp::WireGuard)); + PTF_ASSERT_NOT_NULL(origCookieReplyMessage); + + PTF_ASSERT_EQUAL(newCookieReplyMessage.getDataLen(), origCookieReplyMessage->getDataLen()); + PTF_ASSERT_EQUAL(newCookieReplyMessage.getReceiverIndex(), 0xab7df406); + PTF_ASSERT_TRUE(std::memcmp(newCookieReplyMessage.getNonce().data(), nonce, sizeof(nonce)) == 0); + PTF_ASSERT_TRUE( + std::memcmp(newCookieReplyMessage.getEncryptedCookie().data(), encryptedCookie, sizeof(encryptedCookie)) == 0); + PTF_ASSERT_TRUE(wgCookieReplyPacket.addLayer(&newCookieReplyMessage)); + + PTF_ASSERT_EQUAL(wgCookieReplyPacket.getRawPacket()->getRawDataLen(), bufferLength3); + PTF_ASSERT_EQUAL(newCookieReplyMessage.getDataLen(), origCookieReplyMessage->getDataLen()); + + PTF_ASSERT_BUF_COMPARE(newCookieReplyMessage.getData(), origCookieReplyMessage->getData(), + origCookieReplyMessage->getDataLen()); + + delete origCookieReplyMessage; + + // create WireGuard Transport Data message + + memcpy(origBuffer, buffer4, bufferLength4); + + uint64_t expectedCounterTransport = 0x0000000000000000; + uint8_t expectedEncryptedDataTransport[112] = { + 0xa4, 0xeb, 0xc1, 0x2e, 0xe3, 0xf9, 0x90, 0xda, 0x18, 0x03, 0x3a, 0x07, 0x89, 0xc0, 0x4e, 0x27, + 0x00, 0xf6, 0xf5, 0xc2, 0x71, 0xd4, 0x2a, 0xc4, 0xb4, 0xd6, 0x26, 0x2e, 0x66, 0x65, 0x49, 0xb4, + 0x45, 0xa7, 0x43, 0x6e, 0x82, 0x9b, 0xff, 0xb6, 0xac, 0x65, 0xf0, 0x56, 0x48, 0xbc, 0x0c, 0x39, + 0x1f, 0xe7, 0xc5, 0x88, 0x48, 0x74, 0x37, 0x61, 0x27, 0x16, 0x49, 0x40, 0x18, 0x8f, 0x03, 0xdb, + 0xa6, 0x7a, 0xf8, 0x38, 0x8e, 0xaa, 0xb7, 0x6c, 0x59, 0x36, 0x28, 0xbf, 0x9d, 0xc7, 0xbe, 0x03, + 0x34, 0x6d, 0x91, 0x2e, 0x91, 0x6d, 0xad, 0x86, 0x25, 0x45, 0x45, 0x47, 0x01, 0x36, 0x4f, 0x2d, + 0x24, 0x86, 0xd7, 0xce, 0xd4, 0xc8, 0x64, 0x2c, 0xe5, 0x47, 0xdd, 0xb2, 0x6e, 0xf6, 0xa4, 0x6b + }; + + pcpp::WireGuardTransportDataLayer newTransportDataMessage(0x06f47dab, expectedCounterTransport, + expectedEncryptedDataTransport, 112); + pcpp::Packet wgTransportDataPacket(&rawPacket4); + + auto origTransportDataMessage = + dynamic_cast(wgTransportDataPacket.detachLayer(pcpp::WireGuard)); + PTF_ASSERT_NOT_NULL(origTransportDataMessage); + PTF_ASSERT_EQUAL(newTransportDataMessage.getDataLen(), origTransportDataMessage->getDataLen()); + PTF_ASSERT_EQUAL(newCookieReplyMessage.getReceiverIndex(), 0xab7df406); + PTF_ASSERT_EQUAL(newTransportDataMessage.getCounter(), expectedCounterTransport); + PTF_ASSERT_BUF_COMPARE(newTransportDataMessage.getEncryptedData(), expectedEncryptedDataTransport, 112); + PTF_ASSERT_TRUE(wgTransportDataPacket.addLayer(&newTransportDataMessage)); + + PTF_ASSERT_EQUAL(wgTransportDataPacket.getRawPacket()->getRawDataLen(), bufferLength4); + PTF_ASSERT_EQUAL(newTransportDataMessage.getDataLen(), origTransportDataMessage->getDataLen()); + + PTF_ASSERT_BUF_COMPARE(newTransportDataMessage.getData(), origTransportDataMessage->getData(), + origTransportDataMessage->getDataLen()); + + delete origTransportDataMessage; +} diff --git a/Tests/Packet++Test/main.cpp b/Tests/Packet++Test/main.cpp index b5fdf00748..1b6bc08fba 100644 --- a/Tests/Packet++Test/main.cpp +++ b/Tests/Packet++Test/main.cpp @@ -334,5 +334,11 @@ int main(int argc, char* argv[]) PTF_RUN_TEST(LdapParsingTest, "ldap"); PTF_RUN_TEST(LdapCreationTest, "ldap"); + PTF_RUN_TEST(WireGuardHandshakeInitParsingTest, "wg"); + PTF_RUN_TEST(WireGuardHandshakeRespParsingTest, "wg"); + PTF_RUN_TEST(WireGuardCookieReplyParsingTest, "wg"); + PTF_RUN_TEST(WireGuardTransportDataParsingTest, "wg"); + PTF_RUN_TEST(WireGuardCreationTest, "wg"); + PTF_END_RUNNING_TESTS; }