diff --git a/src/common/network/BaseNetwork.cpp b/src/common/network/BaseNetwork.cpp index 3cdb384d..d98c52d5 100644 --- a/src/common/network/BaseNetwork.cpp +++ b/src/common/network/BaseNetwork.cpp @@ -15,6 +15,7 @@ #include "common/nxdn/NXDNDefines.h" #include "common/p25/dfsi/DFSIDefines.h" #include "common/p25/dfsi/LC.h" +#include "common/p25/kmm/KMMModifyKey.h" #include "network/BaseNetwork.h" #include "Utils.h" @@ -112,6 +113,37 @@ bool BaseNetwork::writeGrantReq(const uint8_t mode, const uint32_t srcId, const return writeMaster({ NET_FUNC::GRANT_REQ, NET_SUBFUNC::NOP }, buffer, MSG_HDR_SIZE, RTP_END_OF_CALL_SEQ, 0U); } +/* Writes enc. key request to the network. */ + +bool BaseNetwork::writeKeyReq(const uint16_t kId, const uint8_t algId) +{ + using namespace p25::defines; + using namespace p25::kmm; + + if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING) + return false; + + uint8_t buffer[DATA_PACKET_LENGTH]; + ::memset(buffer, 0x00U, DATA_PACKET_LENGTH); + + KMMModifyKey modifyKeyCmd = KMMModifyKey(); + modifyKeyCmd.setDecryptInfoFmt(KMM_DECRYPT_INSTRUCT_NONE); + modifyKeyCmd.setAlgId(algId); + modifyKeyCmd.setKId(kId); + + KeysetItem ks = KeysetItem(); + ks.keysetId(0U); + ks.algId(algId); + ks.keyLength(P25DEF::MAX_ENC_KEY_LENGTH_BYTES); + modifyKeyCmd.setKeysetItem(ks); + + modifyKeyCmd.encode(buffer + 11U); + + Utils::dump("writeKeyReq", buffer, modifyKeyCmd.length() + 11U); + + return writeMaster({ NET_FUNC::KEY_REQ, NET_SUBFUNC::NOP }, buffer, modifyKeyCmd.length() + 11U, RTP_END_OF_CALL_SEQ, 0U); +} + /* Writes the local activity log to the network. */ bool BaseNetwork::writeActLog(const char* message) diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index 8919b8c5..bdac4713 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -59,6 +59,7 @@ #define TAG_REPEATER_PING "RPTP" #define TAG_REPEATER_GRANT "RPTG" +#define TAG_REPEATER_KEY "RKEY" #define TAG_TRANSFER "TRNS" #define TAG_TRANSFER_ACT_LOG "TRNSLOG" @@ -172,6 +173,14 @@ namespace network */ bool writeGrantReq(const uint8_t mode, const uint32_t srcId, const uint32_t dstId, const uint8_t slot, const bool unitToUnit); + /** + * @brief Writes a enc. key request to the network. + * @param kId Key ID. + * @param algId Algorithm ID. + * @returns bool True, if request was sent, otherwise false. + */ + bool writeKeyReq(const uint16_t kId, const uint8_t algId); + /** * @brief Writes the local activity log to the network. * @param message Textual string to send as activity log information. diff --git a/src/common/p25/P25Defines.h b/src/common/p25/P25Defines.h index 6e7b2505..bd69f1da 100644 --- a/src/common/p25/P25Defines.h +++ b/src/common/p25/P25Defines.h @@ -117,6 +117,8 @@ namespace p25 const uint8_t AUTH_RAND_SEED_LENGTH_BYTES = 10U; const uint8_t AUTH_RAND_CHLNG_LENGTH_BYTES = 5U; const uint8_t AUTH_KEY_LENGTH_BYTES = 16U; + + const uint8_t MAX_ENC_KEY_LENGTH_BYTES = 32U; /* @} */ /** @name Thresholds */ @@ -143,6 +145,12 @@ namespace p25 /** @name Encryption Algorithms */ /** @brief Unencrypted */ const uint8_t ALGO_UNENCRYPT = 0x80U; + /** @brief DES-OFB */ + const uint8_t ALGO_DES = 0x81U; + /** @brief AES-256 */ + const uint8_t ALGO_AES_256 = 0x84U; + /** @brief ARC4 */ + const uint8_t ALGO_ARC4 = 0xAAU; /** @} */ /** @name IDEN Table Bandwidth Sizes */ @@ -455,6 +463,28 @@ namespace p25 }; } + /** @brief KMM Inventory Type */ + namespace KMM_InventoryType { + enum : uint8_t { + NULL_INVENTORY = 0x00U, //! Null + + LIST_ACTIVE_KEYSET_IDS = 0x01U, //! List Active Keyset IDs + LIST_INACTIVE_KEYSET_IDS = 0x02U, //! List Inactive Keyset IDs + LIST_ACTIVE_KEY_IDS = 0x03U, //! List Active Key IDs + LIST_INACTIVE_KEY_IDS = 0x04U, //! List Inactive Key IDs + }; + } + + /** @brief KMM Hello Flag */ + namespace KMM_HelloFlag { + enum : uint8_t { + IDENT_ONLY = 0x00U, //! KMF or SU Identification Only + + REKEY_REQUEST_UKEK = 0x01U, //! Rekey Request (UKEK Exists) + REKEY_REQUEST_NO_UKEK = 0x02U, //! Rekey Request (UKEK does not exist) + }; + } + /** @brief KMM Decryption Instruction - None */ const uint8_t KMM_DECRYPT_INSTRUCT_NONE = 0x00U; /** @brief KMM Decryption Instruction - Message Indicator */ diff --git a/src/common/p25/kmm/KMMFactory.cpp b/src/common/p25/kmm/KMMFactory.cpp new file mode 100644 index 00000000..b4fdd203 --- /dev/null +++ b/src/common/p25/kmm/KMMFactory.cpp @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "Defines.h" +#include "p25/P25Defines.h" +#include "p25/kmm/KMMFactory.h" +#include "Log.h" +#include "Utils.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::kmm; + +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the KMMFactory class. */ + +KMMFactory::KMMFactory() = default; + +/* Finalizes a instance of KMMFactory class. */ + +KMMFactory::~KMMFactory() = default; + +/* Create an instance of a KMMFrame. */ + +std::unique_ptr KMMFactory::create(const uint8_t* data) +{ + assert(data != nullptr); + + uint8_t messageId = data[0U]; // Message ID + + switch (messageId) { + case KMM_MessageType::HELLO: + return decode(new KMMHello(), data); + case KMM_MessageType::INVENTORY_CMD: + return decode(new KMMInventoryCommand(), data); + case KMM_MessageType::MODIFY_KEY_CMD: + return decode(new KMMModifyKey(), data); + default: + LogError(LOG_P25, "KMMFactory::create(), unknown KMM message ID value, messageId = $%02X", messageId); + break; + } + + return nullptr; +} + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- + +/* Decode a SNDCP packet. */ + +std::unique_ptr KMMFactory::decode(KMMFrame* packet, const uint8_t* data) +{ + assert(packet != nullptr); + assert(data != nullptr); + + if (!packet->decode(data)) { + return nullptr; + } + + return std::unique_ptr(packet); +} diff --git a/src/common/p25/kmm/KMMFactory.h b/src/common/p25/kmm/KMMFactory.h new file mode 100644 index 00000000..c98db52f --- /dev/null +++ b/src/common/p25/kmm/KMMFactory.h @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file KMMFactory.h + * @ingroup p25_kmm + * @file KMMFactory.cpp + * @ingroup p25_kmm + */ +#if !defined(__P25_KMM__KMM_FACTORY_H__) +#define __P25_KMM__KMM_FACTORY_H__ + +#include "common/Defines.h" + +#include "common/p25/kmm/KeysetItem.h" + +#include "common/p25/kmm/KMMFrame.h" +#include "common/p25/kmm/KMMHello.h" +#include "common/p25/kmm/KMMInventoryCommand.h" +#include "common/p25/kmm/KMMModifyKey.h" + +namespace p25 +{ + namespace kmm + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Helper class to instantiate an instance of a KMM frame packet. + * @ingroup p25_kmm + */ + class HOST_SW_API KMMFactory { + public: + /** + * @brief Initializes a new instance of the KMMFactory class. + */ + KMMFactory(); + /** + * @brief Finalizes a instance of the KMMFactory class. + */ + ~KMMFactory(); + + /** + * @brief Create an instance of a KMMFrame. + * @param[in] data Buffer containing KMM frame packet data to decode. + * @returns KMMFrame* Instance of a KMMFrame representing the decoded data. + */ + static std::unique_ptr create(const uint8_t* data); + + private: + /** + * @brief Decode a KMM frame. + * @param packet Instance of KMMFrame. + * @param[in] data Buffer containing KMM frame packet data to decode. + * @returns KMMFrame* Instance of a KMMFrame representing the decoded data. + */ + static std::unique_ptr decode(KMMFrame* packet, const uint8_t* data); + }; + } // namespace kmm +} // namespace p25 + +#endif // __P25_KMM__KMM_FACTORY_H__ diff --git a/src/common/p25/kmm/KMMFrame.cpp b/src/common/p25/kmm/KMMFrame.cpp index 10cd7f29..b86800fb 100644 --- a/src/common/p25/kmm/KMMFrame.cpp +++ b/src/common/p25/kmm/KMMFrame.cpp @@ -33,7 +33,7 @@ KMMFrame::KMMFrame(const KMMFrame& data) : KMMFrame() KMMFrame::KMMFrame() : m_messageId(KMM_MessageType::NULL_CMD), - m_messageLength(7U), + m_messageLength(KMM_FRAME_LENGTH), m_respKind(KMM_ResponseKind::NONE), m_complete(true), m_mfMessageNumber(0U), @@ -50,20 +50,20 @@ KMMFrame::~KMMFrame() = default; // Protected Class Members // --------------------------------------------------------------------------- -/* Internal helper to decode a SNDCP header. */ +/* Internal helper to decode a KMM header. */ -bool KMMFrame::decodeHeader(const uint8_t* data, bool outbound) +bool KMMFrame::decodeHeader(const uint8_t* data) { assert(data != nullptr); m_messageId = data[0U]; // Message ID m_messageLength = __GET_UINT16B(data, 1U); // Message Length - m_respKind = (data[2U] >> 6U) & 0x03U; // Response Kind - m_mfMessageNumber = (data[2U] >> 4U) & 0x03U; // Message Number - m_mfMac = (data[2U] >> 2U) & 0x03U; // MAC + m_respKind = (data[3U] >> 6U) & 0x03U; // Response Kind + m_mfMessageNumber = (data[3U] >> 4U) & 0x03U; // Message Number + m_mfMac = (data[3U] >> 2U) & 0x03U; // MAC - bool done = (data[2U] & 0x01U) == 0x01U; // Done Flag + bool done = (data[3U] & 0x01U) == 0x01U; // Done Flag if (!done) m_complete = true; else @@ -75,16 +75,16 @@ bool KMMFrame::decodeHeader(const uint8_t* data, bool outbound) return true; } -/* Internal helper to encode a SNDCP header. */ +/* Internal helper to encode a KMM header. */ -void KMMFrame::encodeHeader(uint8_t* data, bool outbound) +void KMMFrame::encodeHeader(uint8_t* data) { assert(data != nullptr); data[0U] = m_messageId; // Message ID - __SET_UINT16B(m_messageLength, data, 2U); // Message Length + __SET_UINT16B(m_messageLength, data, 1U); // Message Length - data[2U] = ((m_respKind & 0x03U) << 6U) + // Response Kind + data[3U] = ((m_respKind & 0x03U) << 6U) + // Response Kind ((m_mfMessageNumber & 0x03U) << 4U) + // Message Number ((m_mfMac & 0x03U) << 2U) + // MAC ((!m_complete) ? 0x01U : 0x00U); // Done Flag diff --git a/src/common/p25/kmm/KMMFrame.h b/src/common/p25/kmm/KMMFrame.h index ef9ab0aa..9c6f3900 100644 --- a/src/common/p25/kmm/KMMFrame.h +++ b/src/common/p25/kmm/KMMFrame.h @@ -121,16 +121,14 @@ namespace p25 /** * @brief Internal helper to decode a KMM header. * @param data Buffer containing KMM packet data to decode. - * @param outbound Flag indicating whether the packet is inbound or outbound. * @returns bool True, if decoded, otherwise false. */ - bool decodeHeader(const uint8_t* data, bool outbound = false); + bool decodeHeader(const uint8_t* data); /** * @brief Internal helper to encode a KMM header. * @param data Buffer to encode KMM packet data to. - * @param outbound Flag indicating whether the packet is inbound or outbound. */ - void encodeHeader(uint8_t* data, bool outbound = false); + void encodeHeader(uint8_t* data); __PROTECTED_COPY(KMMFrame); }; diff --git a/src/common/p25/kmm/KMMHello.cpp b/src/common/p25/kmm/KMMHello.cpp new file mode 100644 index 00000000..ff179fbe --- /dev/null +++ b/src/common/p25/kmm/KMMHello.cpp @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "Defines.h" +#include "p25/P25Defines.h" +#include "p25/kmm/KMMHello.h" +#include "Log.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::kmm; + +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the KMMHello class. */ + +KMMHello::KMMHello() : KMMFrame(), + m_flag(KMM_HelloFlag::IDENT_ONLY) +{ + m_messageId = KMM_MessageType::HELLO; + m_respKind = KMM_ResponseKind::DELAYED; +} + +/* Finalizes a instance of the KMMHello class. */ + +KMMHello::~KMMHello() = default; + +/* Decode a KMM modify key. */ + +bool KMMHello::decode(const uint8_t* data) +{ + assert(data != nullptr); + + KMMFrame::decodeHeader(data); + + m_flag = data[10U]; // Hello Flag + + return true; +} + +/* Encode a KMM modify key. */ + +void KMMHello::encode(uint8_t* data) +{ + assert(data != nullptr); + m_messageLength = KMM_HELLO_LENGTH; + + KMMFrame::encodeHeader(data); + + data[10U] = m_flag; // Hello Flag +} + +// --------------------------------------------------------------------------- +// Protected Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to copy the the class. */ + +void KMMHello::copy(const KMMHello& data) +{ + m_flag = data.m_flag; +} diff --git a/src/common/p25/kmm/KMMHello.h b/src/common/p25/kmm/KMMHello.h new file mode 100644 index 00000000..316d8650 --- /dev/null +++ b/src/common/p25/kmm/KMMHello.h @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file KMMHello.h + * @ingroup p25_kmm + * @file KMMHello.cpp + * @ingroup p25_kmm + */ +#if !defined(__P25_KMM__KMM_HELLO_H__) +#define __P25_KMM__KMM_HELLO_H__ + +#include "common/Defines.h" +#include "common/p25/kmm/KMMFrame.h" +#include "common/p25/kmm/KeysetItem.h" +#include "common/Utils.h" + +#include +#include + +namespace p25 +{ + namespace kmm + { + // --------------------------------------------------------------------------- + // Constants + // --------------------------------------------------------------------------- + + /** + * @addtogroup p25_kmm + * @{ + */ + + const uint32_t KMM_HELLO_LENGTH = KMM_FRAME_LENGTH + 1U; + + /** @} */ + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + class HOST_SW_API KMMHello : public KMMFrame { + public: + /** + * @brief Initializes a new instance of the KMMHello class. + */ + KMMHello(); + /** + * @brief Finalizes a instance of the KMMHello class. + */ + ~KMMHello(); + + /** + * @brief Decode a KMM hello. + * @param[in] data Buffer containing KMM frame data to decode. + * @returns bool True, if decoded, otherwise false. + */ + bool decode(const uint8_t* data) override; + /** + * @brief Encode a KMM hello. + * @param[out] data Buffer to encode KMM frame data to. + */ + void encode(uint8_t* data) override; + + public: + /** + * @brief + */ + __PROPERTY(uint8_t, flag, Flag); + + __COPY(KMMHello); + }; + } // namespace kmm +} // namespace p25 + +#endif // __P25_KMM__KMM_HELLO_H__ diff --git a/src/common/p25/kmm/KMMInventoryCommand.cpp b/src/common/p25/kmm/KMMInventoryCommand.cpp new file mode 100644 index 00000000..24b7f32b --- /dev/null +++ b/src/common/p25/kmm/KMMInventoryCommand.cpp @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "Defines.h" +#include "p25/P25Defines.h" +#include "p25/kmm/KMMInventoryCommand.h" +#include "Log.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::kmm; + +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the KMMInventoryCommand class. */ + +KMMInventoryCommand::KMMInventoryCommand() : KMMFrame(), + m_inventoryType(KMM_InventoryType::NULL_INVENTORY) +{ + m_messageId = KMM_MessageType::INVENTORY_CMD; + m_respKind = KMM_ResponseKind::IMMEDIATE; +} + +/* Finalizes a instance of the KMMInventoryCommand class. */ + +KMMInventoryCommand::~KMMInventoryCommand() = default; + +/* Decode a KMM modify key. */ + +bool KMMInventoryCommand::decode(const uint8_t* data) +{ + assert(data != nullptr); + + KMMFrame::decodeHeader(data); + + m_inventoryType = data[10U]; // Inventory Type + + return true; +} + +/* Encode a KMM modify key. */ + +void KMMInventoryCommand::encode(uint8_t* data) +{ + assert(data != nullptr); + m_messageLength = KMM_INVENTORY_CMD_LENGTH; + + KMMFrame::encodeHeader(data); + + data[10U] = m_inventoryType; // Inventory Type +} + +// --------------------------------------------------------------------------- +// Protected Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to copy the the class. */ + +void KMMInventoryCommand::copy(const KMMInventoryCommand& data) +{ + m_inventoryType = data.m_inventoryType; +} diff --git a/src/common/p25/kmm/KMMInventoryCommand.h b/src/common/p25/kmm/KMMInventoryCommand.h new file mode 100644 index 00000000..a4a9cd98 --- /dev/null +++ b/src/common/p25/kmm/KMMInventoryCommand.h @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file KMMInventoryCommand.h + * @ingroup p25_kmm + * @file KMMInventoryCommand.cpp + * @ingroup p25_kmm + */ +#if !defined(__P25_KMM__KMM_INVENTORY_COMMAND_H__) +#define __P25_KMM__KMM_INVENTORY_COMMAND_H__ + +#include "common/Defines.h" +#include "common/p25/kmm/KMMFrame.h" +#include "common/p25/kmm/KeysetItem.h" +#include "common/Utils.h" + +#include +#include + +namespace p25 +{ + namespace kmm + { + // --------------------------------------------------------------------------- + // Constants + // --------------------------------------------------------------------------- + + /** + * @addtogroup p25_kmm + * @{ + */ + + const uint32_t KMM_INVENTORY_CMD_LENGTH = KMM_FRAME_LENGTH + 1U; + + /** @} */ + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + class HOST_SW_API KMMInventoryCommand : public KMMFrame { + public: + /** + * @brief Initializes a new instance of the KMMInventoryCommand class. + */ + KMMInventoryCommand(); + /** + * @brief Finalizes a instance of the KMMInventoryCommand class. + */ + ~KMMInventoryCommand(); + + /** + * @brief Decode a KMM inventory command. + * @param[in] data Buffer containing KMM frame data to decode. + * @returns bool True, if decoded, otherwise false. + */ + bool decode(const uint8_t* data) override; + /** + * @brief Encode a KMM inventory command. + * @param[out] data Buffer to encode KMM frame data to. + */ + void encode(uint8_t* data) override; + + public: + /** + * @brief + */ + __PROPERTY(uint8_t, inventoryType, InventoryType); + + __COPY(KMMInventoryCommand); + }; + } // namespace kmm +} // namespace p25 + +#endif // __P25_KMM__KMM_INVENTORY_COMMAND_H__ diff --git a/src/common/p25/kmm/KMMModifyKey.cpp b/src/common/p25/kmm/KMMModifyKey.cpp index 4c86e389..33d96394 100644 --- a/src/common/p25/kmm/KMMModifyKey.cpp +++ b/src/common/p25/kmm/KMMModifyKey.cpp @@ -32,6 +32,8 @@ KMMModifyKey::KMMModifyKey() : KMMFrame(), m_miSet(false), m_mi(nullptr) { + m_messageId = KMM_MessageType::MODIFY_KEY_CMD; + m_mi = new uint8_t[MI_LENGTH_BYTES]; ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); } @@ -46,7 +48,7 @@ KMMModifyKey::~KMMModifyKey() } } -/* Gets the byte length of this KMMFrame. */ +/* Gets the byte length of this KMMModifyKey. */ uint32_t KMMModifyKey::length() const { @@ -64,39 +66,43 @@ bool KMMModifyKey::decode(const uint8_t* data) { assert(data != nullptr); - KMMFrame::decodeHeader(data, false); + KMMFrame::decodeHeader(data); m_decryptInfoFmt = data[10U]; // Decryption Instruction Format m_algId = data[11U]; // Algorithm ID - m_kId = data[12U]; // Key ID + m_kId = __GET_UINT16B(data, 12U); // Key ID uint16_t offset = 0U; if (m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); - ::memcpy(m_mi, data + 13U, MI_LENGTH_BYTES); + ::memcpy(m_mi, data + 14U, MI_LENGTH_BYTES); offset += 9U; } - m_keysetItem.keysetId(data[13U + offset]); - m_keysetItem.algId(data[14U + offset]); - m_keysetItem.keyLength(data[15U + offset]); + m_keysetItem.keysetId(data[14U + offset]); + m_keysetItem.algId(data[15U + offset]); + m_keysetItem.keyLength(data[16U + offset]); - uint8_t keyCount = data[16U + offset]; + uint8_t keyCount = data[17U + offset]; for (uint8_t i = 0U; i < keyCount; i++) { - KeyItem* key = new KeyItem(); + KeyItem key = KeyItem(); UInt8Array __keyPayload = std::make_unique(m_keysetItem.keyLength()); uint8_t* keyPayload = __keyPayload.get(); - uint8_t keyFormat = data[17U + offset]; + uint8_t keyFormat = data[18U + offset]; uint8_t keyNameLen = keyFormat & 0x1FU; - key->keyFormat(keyFormat & 0xE0U); - key->sln(data[18U + offset]); - key->kId(data[19U + offset]); + key.keyFormat(keyFormat & 0xE0U); + + uint16_t sln = __GET_UINT16B(data, 19U + offset); + key.sln(sln); + + uint16_t kId = __GET_UINT16B(data, 21U + offset); + key.kId(kId); - ::memcpy(keyPayload, data + (20U + offset), m_keysetItem.keyLength()); - key->setKey(keyPayload, m_keysetItem.keyLength()); + ::memcpy(keyPayload, data + (23U + offset), m_keysetItem.keyLength()); + key.setKey(keyPayload, m_keysetItem.keyLength()); m_keysetItem.push_back(key); @@ -111,8 +117,9 @@ bool KMMModifyKey::decode(const uint8_t* data) void KMMModifyKey::encode(uint8_t* data) { assert(data != nullptr); + m_messageLength = length(); - KMMFrame::encodeHeader(data, true); + KMMFrame::encodeHeader(data); if (!m_miSet && m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { m_decryptInfoFmt = KMM_DECRYPT_INSTRUCT_NONE; @@ -120,31 +127,31 @@ void KMMModifyKey::encode(uint8_t* data) data[10U] = m_decryptInfoFmt; // Decryption Instruction Format data[11U] = m_algId; // Algorithm ID - data[12U] = m_kId; // Key ID + __SET_UINT16B(m_kId, data, 12U); // Key ID uint16_t offset = 0U; if (m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { - ::memcpy(data + 13U, m_mi, MI_LENGTH_BYTES); + ::memcpy(data + 14U, m_mi, MI_LENGTH_BYTES); offset += 9U; } - data[13U + offset] = m_keysetItem.keysetId(); - data[14U + offset] = m_keysetItem.algId(); - data[15U + offset] = m_keysetItem.keyLength(); + data[14U + offset] = m_keysetItem.keysetId(); + data[15U + offset] = m_keysetItem.algId(); + data[16U + offset] = m_keysetItem.keyLength(); uint8_t keyCount = m_keysetItem.keys().size(); - data[16U + offset] = keyCount; + data[17U + offset] = keyCount; for (auto key : m_keysetItem.keys()) { - uint8_t keyNameLen = key->keyFormat() & 0x1FU; - data[17U + offset] = key->keyFormat(); - data[18U + offset] = key->sln(); - data[19U + offset] = key->kId(); + uint8_t keyNameLen = key.keyFormat() & 0x1FU; + data[18U + offset] = key.keyFormat(); + __SET_UINT16B(key.sln(), data, 19U + offset); + __SET_UINT16B(key.kId(), data, 21U + offset); UInt8Array __keyPayload = std::make_unique(m_keysetItem.keyLength()); uint8_t* keyPayload = __keyPayload.get(); - key->getKey(keyPayload); + key.getKey(keyPayload); - ::memcpy(data + (20U + offset), keyPayload, m_keysetItem.keyLength()); + ::memcpy(data + (23U + offset), keyPayload, m_keysetItem.keyLength()); offset += 5U + keyNameLen + m_keysetItem.keyLength(); } diff --git a/src/common/p25/kmm/KMMModifyKey.h b/src/common/p25/kmm/KMMModifyKey.h index 6e8ae200..5a831e17 100644 --- a/src/common/p25/kmm/KMMModifyKey.h +++ b/src/common/p25/kmm/KMMModifyKey.h @@ -37,7 +37,7 @@ namespace p25 * @{ */ - const uint32_t KMM_MODIFY_KEY_LENGTH = KMM_FRAME_LENGTH + 4U; + const uint32_t KMM_MODIFY_KEY_LENGTH = KMM_FRAME_LENGTH + 8U; /** @} */ @@ -63,16 +63,16 @@ namespace p25 uint32_t length() const override; /** - * @brief Decode a KMM modify key packet. - * @param[in] data Buffer containing KMM packet data to decode. + * @brief Decode a KMM modify key. + * @param[in] data Buffer containing KMM frame data to decode. * @returns bool True, if decoded, otherwise false. */ - bool decode(const uint8_t* data); + bool decode(const uint8_t* data) override; /** - * @brief Encode a KMM modify key packet. - * @param[out] data Buffer to encode KMM packet data to. + * @brief Encode a KMM modify key. + * @param[out] data Buffer to encode KMM frame data to. */ - void encode(uint8_t* data); + void encode(uint8_t* data) override; /** @name Encryption data */ /** diff --git a/src/common/p25/kmm/KeysetItem.h b/src/common/p25/kmm/KeysetItem.h index 395ed00d..d3c556e5 100644 --- a/src/common/p25/kmm/KeysetItem.h +++ b/src/common/p25/kmm/KeysetItem.h @@ -24,6 +24,19 @@ namespace p25 { namespace kmm { + // --------------------------------------------------------------------------- + // Constants + // --------------------------------------------------------------------------- + + /** + * @addtogroup p25_kmm + * @{ + */ + + const uint8_t MAX_ENC_KEY_LENGTH_BYTES = 32U; + + /** @} */ + // --------------------------------------------------------------------------- // Class Declaration // --------------------------------------------------------------------------- @@ -42,19 +55,9 @@ namespace p25 m_sln(0U), m_kId(0U), m_keyLength(0U), - m_keyMaterial(nullptr) + m_keyMaterial() { - /* stub */ - } - /** - * @brief Finalizes a instance of the KeyItem class. - */ - ~KeyItem() - { - if (m_keyMaterial != nullptr) { - delete[] m_keyMaterial; - m_keyMaterial = nullptr; - } + ::memset(m_keyMaterial, 0x00U, MAX_ENC_KEY_LENGTH_BYTES); } /** @@ -69,11 +72,8 @@ namespace p25 m_kId = data.m_kId; if (data.m_keyLength > 0U) { - if (m_keyMaterial != nullptr) { - delete[] m_keyMaterial; - } + ::memset(m_keyMaterial, 0x00U, MAX_ENC_KEY_LENGTH_BYTES); - m_keyMaterial = new uint8_t[data.m_keyLength]; m_keyLength = data.m_keyLength; ::memcpy(m_keyMaterial, data.m_keyMaterial, data.m_keyLength); } @@ -91,6 +91,10 @@ namespace p25 { assert(key != nullptr); m_keyLength = keyLength; + if (m_keyMaterial == nullptr) { + ::memset(m_keyMaterial, 0x00U, MAX_ENC_KEY_LENGTH_BYTES); + ::memset(m_keyMaterial, 0x00U, m_keyLength); + } ::memcpy(m_keyMaterial, key, keyLength); } @@ -120,7 +124,7 @@ namespace p25 private: uint32_t m_keyLength; - uint8_t* m_keyMaterial; + uint8_t m_keyMaterial[MAX_ENC_KEY_LENGTH_BYTES]; }; // --------------------------------------------------------------------------- @@ -144,15 +148,6 @@ namespace p25 { /* stub */ } - /** - * @brief Finalizes a instance of the KeysetItem class. - */ - ~KeysetItem() - { - for (auto key : m_keys) { - delete key; - } - } /** * @brief Equals operator. Copies this KeysetItem to another KeysetItem. @@ -165,13 +160,9 @@ namespace p25 m_algId = data.m_algId; m_keyLength = data.m_keyLength; - for (auto key : m_keys) { - delete key; - } m_keys.clear(); - for (auto key : data.m_keys) { - KeyItem* copy = key; + KeyItem copy = key; m_keys.push_back(copy); } } @@ -198,7 +189,7 @@ namespace p25 * @brief Add a key to the key list. * @param key */ - void push_back(KeyItem* key) + void push_back(KeyItem key) { m_keys.push_back(key); } @@ -220,7 +211,7 @@ namespace p25 /** * @brief List of keys. */ - __PROPERTY_PLAIN(std::vector, keys); + __PROPERTY_PLAIN(std::vector, keys); }; } // namespace kmm } // namespace p25 diff --git a/src/common/p25/sndcp/SNDCPCtxActAccept.h b/src/common/p25/sndcp/SNDCPCtxActAccept.h index 54752066..a913d6e2 100644 --- a/src/common/p25/sndcp/SNDCPCtxActAccept.h +++ b/src/common/p25/sndcp/SNDCPCtxActAccept.h @@ -46,12 +46,12 @@ namespace p25 * @param[in] data Buffer containing SNDCP packet data to decode. * @returns bool True, if decoded, otherwise false. */ - bool decode(const uint8_t* data); + bool decode(const uint8_t* data) override; /** * @brief Encode a SNDCP context activation response. * @param[out] data Buffer to encode SNDCP packet data to. */ - void encode(uint8_t* data); + void encode(uint8_t* data) override; public: /** diff --git a/src/common/p25/sndcp/SNDCPCtxActReject.h b/src/common/p25/sndcp/SNDCPCtxActReject.h index f16415fe..dc9f6047 100644 --- a/src/common/p25/sndcp/SNDCPCtxActReject.h +++ b/src/common/p25/sndcp/SNDCPCtxActReject.h @@ -46,12 +46,12 @@ namespace p25 * @param[in] data Buffer containing SNDCP packet data to decode. * @returns bool True, if decoded, otherwise false. */ - bool decode(const uint8_t* data); + bool decode(const uint8_t* data) override; /** * @brief Encode a SNDCP context activation reject packet. * @param[out] data Buffer to encode SNDCP packet data to. */ - void encode(uint8_t* data); + void encode(uint8_t* data) override; public: /** diff --git a/src/common/p25/sndcp/SNDCPCtxActRequest.h b/src/common/p25/sndcp/SNDCPCtxActRequest.h index 65ae9a22..9022a37d 100644 --- a/src/common/p25/sndcp/SNDCPCtxActRequest.h +++ b/src/common/p25/sndcp/SNDCPCtxActRequest.h @@ -46,12 +46,12 @@ namespace p25 * @param[in] data Buffer containing SNDCP packet data to decode. * @returns bool True, if decoded, otherwise false. */ - bool decode(const uint8_t* data); + bool decode(const uint8_t* data) override; /** * @brief Encode a SNDCP context activation request. * @param[out] data Buffer to encode SNDCP packet data to. */ - void encode(uint8_t* data); + void encode(uint8_t* data) override; public: /** diff --git a/src/common/p25/sndcp/SNDCPCtxDeactivation.h b/src/common/p25/sndcp/SNDCPCtxDeactivation.h index 5a53b710..06b1a00c 100644 --- a/src/common/p25/sndcp/SNDCPCtxDeactivation.h +++ b/src/common/p25/sndcp/SNDCPCtxDeactivation.h @@ -46,12 +46,12 @@ namespace p25 * @param[in] data Buffer containing SNDCP packet data to decode. * @returns bool True, if decoded, otherwise false. */ - bool decode(const uint8_t* data); + bool decode(const uint8_t* data) override; /** * @brief Encode a SNDCP context deactivation packet. * @param[out] data Buffer to encode SNDCP packet data to. */ - void encode(uint8_t* data); + void encode(uint8_t* data) override; public: /** diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index b131e92f..7bcdbe13 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -573,7 +573,7 @@ bool HostFNE::createMasterNetwork() parrotDelay, parrotGrantDemand, m_allowActivityTransfer, m_allowDiagnosticTransfer, m_pingTime, m_updateLookupTime); m_network->setOptions(masterConf, true); - m_network->setLookups(m_ridLookup, m_tidLookup, m_peerListLookup); + m_network->setLookups(m_ridLookup, m_tidLookup, m_peerListLookup, m_cryptoLookup); if (m_RESTAPI != nullptr) { m_RESTAPI->setNetwork(m_network); diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 00c743e7..9f7042c3 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -10,6 +10,7 @@ #include "fne/Defines.h" #include "common/edac/SHA256.h" #include "common/network/json/json.h" +#include "common/p25/kmm/KMMFactory.h" #include "common/zlib/zlib.h" #include "common/Log.h" #include "common/Utils.h" @@ -209,13 +210,15 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions) } } -/* Sets the instances of the Radio ID, Talkgroup Rules, and Peer List lookup tables. */ +/* Sets the instances of the Radio ID, Talkgroup ID Peer List, and Crypto lookup tables. */ -void FNENetwork::setLookups(lookups::RadioIdLookup* ridLookup, lookups::TalkgroupRulesLookup* tidLookup, lookups::PeerListLookup* peerListLookup) +void FNENetwork::setLookups(lookups::RadioIdLookup* ridLookup, lookups::TalkgroupRulesLookup* tidLookup, lookups::PeerListLookup* peerListLookup, + CryptoContainer* cryptoLookup) { m_ridLookup = ridLookup; m_tidLookup = tidLookup; m_peerListLookup = peerListLookup; + m_cryptoLookup = cryptoLookup; } /* Sets endpoint preshared encryption key. */ @@ -1061,13 +1064,88 @@ void* FNENetwork::threadedNetworkRx(void* arg) } } break; - case NET_FUNC::INCALL_CTRL: + + case NET_FUNC::INCALL_CTRL: // In-Call Control { // FNEs are god-like entities, and we don't recognize the authority of foreign FNEs telling us what // to do... } break; + case NET_FUNC::KEY_REQ: // Enc. Key Request + { + using namespace p25::defines; + using namespace p25::kmm; + + if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) { + FNEPeerConnection* connection = network->m_peers[peerId]; + if (connection != nullptr) { + std::string ip = udp::Socket::address(req->address); + + // validate peer (simple validation really) + if (connection->connected() && connection->address() == ip) { + std::unique_ptr frame = KMMFactory::create(req->buffer + 11U); + if (frame == nullptr) { + LogWarning(LOG_NET, "PEER %u (%s), undecodable KMM frame from peer", peerId, connection->identity().c_str()); + break; + } + + switch (frame->getMessageId()) { + case P25DEF::KMM_MessageType::MODIFY_KEY_CMD: + { + KMMModifyKey* modifyKey = static_cast(frame.get()); + if (modifyKey->getAlgId() > 0U && modifyKey->getKId() > 0U) { + LogMessage(LOG_NET, "PEER %u (%s) requested enc. key, algId = $%02X, kID = $%04X", peerId, connection->identity().c_str(), + modifyKey->getAlgId(), modifyKey->getKId()); + ::KeyItem keyItem = network->m_cryptoLookup->find(modifyKey->getKId()); + if (!keyItem.isInvalid()) { + uint8_t buffer[DATA_PACKET_LENGTH]; + ::memset(buffer, 0x00U, DATA_PACKET_LENGTH); + + KMMModifyKey modifyKeyRsp = KMMModifyKey(); + modifyKeyRsp.setDecryptInfoFmt(KMM_DECRYPT_INSTRUCT_NONE); + modifyKeyRsp.setAlgId(modifyKey->getAlgId()); + modifyKeyRsp.setKId(0U); + + KeysetItem ks = KeysetItem(); + ks.keysetId(1U); + ks.algId(modifyKey->getAlgId()); + ks.keyLength(P25DEF::MAX_ENC_KEY_LENGTH_BYTES); // bryanb: this --- will be problematic and should be properly calculated + + p25::kmm::KeyItem ki = p25::kmm::KeyItem(); + ki.keyFormat(KEY_FORMAT_TEK); + ki.kId((uint16_t)keyItem.kId()); + ki.sln((uint16_t)keyItem.sln()); + + uint8_t key[P25DEF::MAX_ENC_KEY_LENGTH_BYTES]; + ::memset(key, 0x00U, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); + keyItem.getKey(key); + ki.setKey(key, P25DEF::MAX_ENC_KEY_LENGTH_BYTES); // bryanb: this --- will be problematic and should be properly calculated + + ks.push_back(ki); + modifyKeyRsp.setKeysetItem(ks); + + modifyKeyRsp.encode(buffer + 11U); + + network->writePeer(peerId, { NET_FUNC::KEY_RSP, NET_SUBFUNC::NOP }, buffer, modifyKeyRsp.length() + 11U, + RTP_END_OF_CALL_SEQ, network->createStreamId(), false, false, true); + } + } + } + break; + + default: + break; + } + } + else { + network->writePeerNAK(peerId, streamId, TAG_REPEATER_KEY, NET_CONN_NAK_FNE_UNAUTHORIZED); + } + } + } + } + break; + case NET_FUNC::TRANSFER: { // are activity/diagnostic transfers occurring from the alternate port? diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 02a73ea4..dc88850c 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -32,6 +32,7 @@ #include "common/lookups/TalkgroupRulesLookup.h" #include "common/lookups/PeerListLookup.h" #include "fne/network/influxdb/InfluxDB.h" +#include "fne/CryptoContainer.h" #include "host/network/Network.h" #include @@ -456,12 +457,14 @@ namespace network callhandler::TagNXDNData* nxdnTrafficHandler() const { return m_tagNXDN; } /** - * @brief Sets the instances of the Radio ID, Talkgroup ID and Peer List lookup tables. + * @brief Sets the instances of the Radio ID, Talkgroup ID Peer List, and Crypto lookup tables. * @param ridLookup Radio ID Lookup Table Instance * @param tidLookup Talkgroup Rules Lookup Table Instance * @param peerListLookup Peer List Lookup Table Instance + * @param cryptoLookup Crypto Container Lookup Table Instance */ - void setLookups(lookups::RadioIdLookup* ridLookup, lookups::TalkgroupRulesLookup* tidLookup, lookups::PeerListLookup* peerListLookup); + void setLookups(lookups::RadioIdLookup* ridLookup, lookups::TalkgroupRulesLookup* tidLookup, lookups::PeerListLookup* peerListLookup, + CryptoContainer* cryptoLookup); /** * @brief Sets endpoint preshared encryption key. * @param presharedKey Encryption preshared key for networking. @@ -537,6 +540,8 @@ namespace network lookups::TalkgroupRulesLookup* m_tidLookup; lookups::PeerListLookup* m_peerListLookup; + CryptoContainer* m_cryptoLookup; + NET_CONN_STATUS m_status; static std::mutex m_peerMutex; diff --git a/src/host/network/Network.cpp b/src/host/network/Network.cpp index 0df7879a..632e71f9 100644 --- a/src/host/network/Network.cpp +++ b/src/host/network/Network.cpp @@ -13,6 +13,7 @@ #include "common/network/RTPHeader.h" #include "common/network/RTPFNEHeader.h" #include "common/network/json/json.h" +#include "common/p25/kmm/KMMFactory.h" #include "common/Log.h" #include "common/Utils.h" #include "network/Network.h" @@ -74,7 +75,8 @@ Network::Network(const std::string& address, uint16_t port, uint16_t localPort, m_promiscuousPeer(false), m_dmrInCallCallback(nullptr), m_p25InCallCallback(nullptr), - m_nxdnInCallCallback(nullptr) + m_nxdnInCallCallback(nullptr), + m_keyRespCallback(nullptr) { assert(!address.empty()); assert(port > 0U); @@ -592,6 +594,44 @@ void Network::clock(uint32_t ms) } break; + case NET_FUNC::KEY_RSP: // Enc. Key Response + { + if (m_enabled) { + using namespace p25::kmm; + + std::unique_ptr frame = KMMFactory::create(buffer.get() + 11U); + if (frame == nullptr) { + LogWarning(LOG_NET, "PEER %u, undecodable KMM frame from master", m_peerId); + break; + } + + switch (frame->getMessageId()) { + case P25DEF::KMM_MessageType::MODIFY_KEY_CMD: + { + KMMModifyKey* modifyKey = static_cast(frame.get()); + if (modifyKey->getAlgId() > 0U) { + KeysetItem ks = modifyKey->getKeysetItem(); + if (ks.keys().size() > 0U) { + // fetch first key (a master response should never really send back more then one key) + KeyItem ki = ks.keys()[0]; + LogMessage(LOG_NET, "PEER %u, master reported enc. key, algId = $%02X, kID = $%04X", m_peerId, + ks.algId(), ki.kId()); + + if (m_keyRespCallback != nullptr) { + m_keyRespCallback(ki, ks.algId(), ks.keyLength()); + } + } + } + } + break; + + default: + break; + } + } + } + break; + case NET_FUNC::NAK: // Master Negative Ack { // DVM 3.6 adds support to respond with a NAK reason, as such we just check if the NAK response is greater diff --git a/src/host/network/Network.h b/src/host/network/Network.h index 18182782..53489cad 100644 --- a/src/host/network/Network.h +++ b/src/host/network/Network.h @@ -25,6 +25,7 @@ #include "common/network/BaseNetwork.h" #include "common/lookups/RadioIdLookup.h" #include "common/lookups/TalkgroupRulesLookup.h" +#include "common/p25/kmm/KeysetItem.h" #include #include @@ -165,6 +166,12 @@ namespace network */ void setNXDNICCCallback(std::function&& callback) { m_nxdnInCallCallback = callback; } + /** + * @brief Helper to set the enc. key response callback. + * @param callback + */ + void setKeyResponseCallback(std::function&& callback) { m_keyRespCallback = callback; } + public: /** * @brief Last received RTP sequence number. @@ -230,6 +237,8 @@ namespace network std::function m_p25InCallCallback; std::function m_nxdnInCallCallback; + std::function m_keyRespCallback; + /** * @brief User overrideable handler that allows user code to process network packets not handled by this class. * @param peerId Peer ID.