diff --git a/configs/config.example.yml b/configs/config.example.yml
index 9b90110d..8a2adecb 100644
--- a/configs/config.example.yml
+++ b/configs/config.example.yml
@@ -45,6 +45,13 @@ network:
# FNE access password.
password: "PASSWORD"
+ # Flag indicating whether or not host endpoint networking is encrypted.
+ encrypted: false
+ # AES-256 32-byte Preshared Key
+ # (This field *must* be 32 hex bytes in length or 64 characters
+ # 0 - 9, A - F.)
+ presharedKey: "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+
# Maximum allowable DMR network jitter.
jitter: 360
diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml
index 0b7e7220..0241a455 100644
--- a/configs/fne-config.example.yml
+++ b/configs/fne-config.example.yml
@@ -46,6 +46,13 @@ master:
# Flag indicating whether or not verbose debug logging is enabled.
debug: false
+ # Flag indicating whether or not master endpoint networking is encrypted.
+ encrypted: false
+ # AES-256 32-byte Preshared Key
+ # (This field *must* be 32 hex bytes in length or 64 characters
+ # 0 - 9, A - F.)
+ presharedKey: "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+
# Flag indicating whether or not DMR traffic will be passed.
allowDMRTraffic: true
# Flag indicating whether or not P25 traffic will be passed.
@@ -89,6 +96,13 @@ peers:
# Network Peer ID
peerId: 9000990
+ # Flag indicating whether or not peer endpoint networking is encrypted.
+ encrypted: false
+ # AES-256 32-byte Preshared Key
+ # (This field *must* be 32 hex bytes in length or 64 characters
+ # 0 - 9, A - F.)
+ presharedKey: "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+
#
rxFrequency: 0
#
diff --git a/src/common/AESCrypto.cpp b/src/common/AESCrypto.cpp
index 0f5e7f61..944bcd23 100644
--- a/src/common/AESCrypto.cpp
+++ b/src/common/AESCrypto.cpp
@@ -34,6 +34,8 @@
*/
#include "Defines.h"
#include "AESCrypto.h"
+#include "Log.h"
+#include "Utils.h"
using namespace crypto;
@@ -272,15 +274,15 @@ static const uint8_t INV_CMDS[4][4] = { {14, 11, 13, 9}, {9, 14, 11, 13}, {13, 9
///
AES::AES(const AESKeyLength keyLength) {
switch (keyLength) {
- case AESKeyLength::AES_128:
+ case AESKeyLength::AES_128:
this->m_Nk = 4;
this->m_Nr = 10;
break;
- case AESKeyLength::AES_192:
+ case AESKeyLength::AES_192:
this->m_Nk = 6;
this->m_Nr = 12;
break;
- case AESKeyLength::AES_256:
+ case AESKeyLength::AES_256:
this->m_Nk = 8;
this->m_Nr = 14;
break;
@@ -294,17 +296,20 @@ AES::AES(const AESKeyLength keyLength) {
///
///
///
-uint8_t* AES::encryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[])
+uint8_t* AES::encryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[])
{
- if (inLen % m_blockBytesLen != 0) {
- throw std::length_error("Plaintext length must be divisible by " + std::to_string(m_blockBytesLen));
+ if (inLen % BLOCK_BYTES_LEN != 0) {
+ LogDebug(LOG_HOST, "AES::encryptECB() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
+ return nullptr;
}
uint8_t* out = new uint8_t[inLen];
+ ::memset(out, 0x00U, inLen);
uint8_t* roundKeys = new uint8_t[4 * AES_NB * (m_Nr + 1)];
+ ::memset(roundKeys, 0x00U, 4 * AES_NB * (m_Nr + 1));
keyExpansion(key, roundKeys);
- for (uint32_t i = 0; i < inLen; i += m_blockBytesLen) {
+ for (uint32_t i = 0; i < inLen; i += BLOCK_BYTES_LEN) {
encryptBlock(in + i, out + i, roundKeys);
}
@@ -321,15 +326,18 @@ uint8_t* AES::encryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[]
///
uint8_t* AES::decryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[])
{
- if (inLen % m_blockBytesLen != 0) {
- throw std::length_error("Plaintext length must be divisible by " + std::to_string(m_blockBytesLen));
+ if (inLen % BLOCK_BYTES_LEN != 0) {
+ LogDebug(LOG_HOST, "AES::decryptECB() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
+ return nullptr;
}
uint8_t* out = new uint8_t[inLen];
+ ::memset(out, 0x00U, inLen);
uint8_t* roundKeys = new uint8_t[4 * AES_NB * (m_Nr + 1)];
+ ::memset(roundKeys, 0x00U, 4 * AES_NB * (m_Nr + 1));
keyExpansion(key, roundKeys);
- for (uint32_t i = 0; i < inLen; i += m_blockBytesLen) {
+ for (uint32_t i = 0; i < inLen; i += BLOCK_BYTES_LEN) {
decryptBlock(in + i, out + i, roundKeys);
}
@@ -347,20 +355,23 @@ uint8_t* AES::decryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[]
///
uint8_t* AES::encryptCBC(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t* iv)
{
- if (inLen % m_blockBytesLen != 0) {
- throw std::length_error("Plaintext length must be divisible by " + std::to_string(m_blockBytesLen));
+ if (inLen % BLOCK_BYTES_LEN != 0) {
+ LogDebug(LOG_HOST, "AES::encryptCBC() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
+ return nullptr;
}
uint8_t* out = new uint8_t[inLen];
- uint8_t block[m_blockBytesLen];
+ ::memset(out, 0x00U, inLen);
+ uint8_t block[BLOCK_BYTES_LEN];
uint8_t* roundKeys = new uint8_t[4 * AES_NB * (m_Nr + 1)];
+ ::memset(roundKeys, 0x00U, 4 * AES_NB * (m_Nr + 1));
keyExpansion(key, roundKeys);
- memcpy(block, iv, m_blockBytesLen);
- for (uint32_t i = 0; i < inLen; i += m_blockBytesLen) {
- xorBlocks(block, in + i, block, m_blockBytesLen);
+ memcpy(block, iv, BLOCK_BYTES_LEN);
+ for (uint32_t i = 0; i < inLen; i += BLOCK_BYTES_LEN) {
+ xorBlocks(block, in + i, block, BLOCK_BYTES_LEN);
encryptBlock(block, out + i, roundKeys);
- memcpy(block, out + i, m_blockBytesLen);
+ memcpy(block, out + i, BLOCK_BYTES_LEN);
}
delete[] roundKeys;
@@ -377,20 +388,23 @@ uint8_t* AES::encryptCBC(const uint8_t in[], uint32_t inLen, const uint8_t key[]
///
uint8_t* AES::decryptCBC(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t *iv)
{
- if (inLen % m_blockBytesLen != 0) {
- throw std::length_error("Plaintext length must be divisible by " + std::to_string(m_blockBytesLen));
+ if (inLen % BLOCK_BYTES_LEN != 0) {
+ LogDebug(LOG_HOST, "AES::decryptCBC() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
+ return nullptr;
}
uint8_t* out = new uint8_t[inLen];
- uint8_t block[m_blockBytesLen];
+ ::memset(out, 0x00U, inLen);
+ uint8_t block[BLOCK_BYTES_LEN];
uint8_t* roundKeys = new uint8_t[4 * AES_NB * (m_Nr + 1)];
+ ::memset(roundKeys, 0x00U, 4 * AES_NB * (m_Nr + 1));
keyExpansion(key, roundKeys);
- memcpy(block, iv, m_blockBytesLen);
- for (uint32_t i = 0; i < inLen; i += m_blockBytesLen) {
+ memcpy(block, iv, BLOCK_BYTES_LEN);
+ for (uint32_t i = 0; i < inLen; i += BLOCK_BYTES_LEN) {
decryptBlock(in + i, out + i, roundKeys);
- xorBlocks(block, out + i, out + i, m_blockBytesLen);
- memcpy(block, in + i, m_blockBytesLen);
+ xorBlocks(block, out + i, out + i, BLOCK_BYTES_LEN);
+ memcpy(block, in + i, BLOCK_BYTES_LEN);
}
delete[] roundKeys;
@@ -407,21 +421,24 @@ uint8_t* AES::decryptCBC(const uint8_t in[], uint32_t inLen, const uint8_t key[]
///
uint8_t* AES::encryptCFB(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t *iv)
{
- if (inLen % m_blockBytesLen != 0) {
- throw std::length_error("Plaintext length must be divisible by " + std::to_string(m_blockBytesLen));
+ if (inLen % BLOCK_BYTES_LEN != 0) {
+ LogDebug(LOG_HOST, "AES::encryptCFB() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
+ return nullptr;
}
uint8_t* out = new uint8_t[inLen];
- uint8_t block[m_blockBytesLen];
- uint8_t encryptedBlock[m_blockBytesLen];
+ ::memset(out, 0x00U, inLen);
+ uint8_t block[BLOCK_BYTES_LEN];
+ uint8_t encryptedBlock[BLOCK_BYTES_LEN];
uint8_t* roundKeys = new uint8_t[4 * AES_NB * (m_Nr + 1)];
+ ::memset(roundKeys, 0x00U, 4 * AES_NB * (m_Nr + 1));
keyExpansion(key, roundKeys);
- memcpy(block, iv, m_blockBytesLen);
- for (uint32_t i = 0; i < inLen; i += m_blockBytesLen) {
+ memcpy(block, iv, BLOCK_BYTES_LEN);
+ for (uint32_t i = 0; i < inLen; i += BLOCK_BYTES_LEN) {
encryptBlock(block, encryptedBlock, roundKeys);
- xorBlocks(in + i, encryptedBlock, out + i, m_blockBytesLen);
- memcpy(block, out + i, m_blockBytesLen);
+ xorBlocks(in + i, encryptedBlock, out + i, BLOCK_BYTES_LEN);
+ memcpy(block, out + i, BLOCK_BYTES_LEN);
}
delete[] roundKeys;
@@ -438,21 +455,24 @@ uint8_t* AES::encryptCFB(const uint8_t in[], uint32_t inLen, const uint8_t key[]
///
uint8_t* AES::decryptCFB(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t *iv)
{
- if (inLen % m_blockBytesLen != 0) {
- throw std::length_error("Plaintext length must be divisible by " + std::to_string(m_blockBytesLen));
+ if (inLen % BLOCK_BYTES_LEN != 0) {
+ LogDebug(LOG_HOST, "AES::decryptCFB() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen);
+ return nullptr;
}
uint8_t* out = new uint8_t[inLen];
- uint8_t block[m_blockBytesLen];
- uint8_t encryptedBlock[m_blockBytesLen];
+ ::memset(out, 0x00U, inLen);
+ uint8_t block[BLOCK_BYTES_LEN];
+ uint8_t encryptedBlock[BLOCK_BYTES_LEN];
uint8_t* roundKeys = new uint8_t[4 * AES_NB * (m_Nr + 1)];
+ ::memset(roundKeys, 0x00U, 4 * AES_NB * (m_Nr + 1));
keyExpansion(key, roundKeys);
- memcpy(block, iv, m_blockBytesLen);
- for (uint32_t i = 0; i < inLen; i += m_blockBytesLen) {
+ memcpy(block, iv, BLOCK_BYTES_LEN);
+ for (uint32_t i = 0; i < inLen; i += BLOCK_BYTES_LEN) {
encryptBlock(block, encryptedBlock, roundKeys);
- xorBlocks(in + i, encryptedBlock, out + i, m_blockBytesLen);
- memcpy(block, in + i, m_blockBytesLen);
+ xorBlocks(in + i, encryptedBlock, out + i, BLOCK_BYTES_LEN);
+ memcpy(block, in + i, BLOCK_BYTES_LEN);
}
delete[] roundKeys;
@@ -633,7 +653,7 @@ void AES::xorWords(uint8_t* a, uint8_t* b, uint8_t* c)
///
///
///
-void AES::rcon(uint8_t *a, uint32_t n)
+void AES::rCon(uint8_t *a, uint32_t n)
{
uint8_t c = 1;
for (uint32_t i = 0; i < n - 1; i++) {
@@ -668,7 +688,7 @@ void AES::keyExpansion(const uint8_t key[], uint8_t w[]) {
if (i / 4 % m_Nk == 0) {
rotWord(temp);
subWord(temp);
- this->rcon(rcon, i / (m_Nk * 4));
+ rCon(rcon, i / (m_Nk * 4));
xorWords(temp, rcon, temp);
} else if (m_Nk > 6 && i / 4 % m_Nk == 4) {
subWord(temp);
diff --git a/src/common/AESCrypto.h b/src/common/AESCrypto.h
index 00178316..86edf792 100644
--- a/src/common/AESCrypto.h
+++ b/src/common/AESCrypto.h
@@ -74,9 +74,9 @@ namespace crypto
///
uint8_t* decryptCFB(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t* iv);
- private:
- static constexpr uint32_t m_blockBytesLen = 4 * AES_NB * sizeof(uint8_t);
+ static constexpr uint32_t BLOCK_BYTES_LEN = 4 * AES_NB * sizeof(uint8_t);
+ private:
uint32_t m_Nk;
uint32_t m_Nr;
@@ -96,7 +96,7 @@ namespace crypto
void rotWord(uint8_t* a);
void xorWords(uint8_t* a, uint8_t* b, uint8_t* c);
- void rcon(uint8_t* a, uint32_t n);
+ void rCon(uint8_t* a, uint32_t n);
void keyExpansion(const uint8_t key[], uint8_t w[]);
diff --git a/src/common/Utils.h b/src/common/Utils.h
index 7689ee6a..9d58fed2 100644
--- a/src/common/Utils.h
+++ b/src/common/Utils.h
@@ -173,16 +173,9 @@ inline std::string strtoupper(const std::string value) {
#define new_unique(type, ...) std::unique_ptr(new type(__VA_ARGS__))
-/// Creates a named unique buffer.
-#define __UNIQUE_BUFFER(name, type, length) \
- std::unique_ptr name = std::unique_ptr(new type[length]); \
- ::memset(name.get(), 0x00U, length);
-
+/// Unique uint8_t array.
typedef std::unique_ptr UInt8Array;
-/// Creates a named uint8_t array buffer.
-#define __UNIQUE_UINT8_ARRAY(name, length) __UNIQUE_BUFFER(name, uint8_t, length)
-
// ---------------------------------------------------------------------------
// Class Declaration
// Implements various helper utilities.
diff --git a/src/common/dmr/lc/CSBK.cpp b/src/common/dmr/lc/CSBK.cpp
index a178ca7a..ac7b9a42 100644
--- a/src/common/dmr/lc/CSBK.cpp
+++ b/src/common/dmr/lc/CSBK.cpp
@@ -220,7 +220,8 @@ ulong64_t CSBK::toValue(const uint8_t* payload)
///
UInt8Array CSBK::fromValue(const ulong64_t value)
{
- __UNIQUE_UINT8_ARRAY(payload, DMR_CSBK_LENGTH_BYTES - 4U);
+ UInt8Array payload = std::unique_ptr(new uint8_t[DMR_CSBK_LENGTH_BYTES - 4U]);
+ ::memset(payload.get(), 0x00U, DMR_CSBK_LENGTH_BYTES - 4U);
// split ulong64_t (8 byte) value into bytes
payload[0U] = (uint8_t)((value >> 56) & 0xFFU);
diff --git a/src/common/network/FrameQueue.cpp b/src/common/network/FrameQueue.cpp
index 6298667e..0923cabc 100644
--- a/src/common/network/FrameQueue.cpp
+++ b/src/common/network/FrameQueue.cpp
@@ -136,7 +136,7 @@ UInt8Array FrameQueue::read(int& messageLength, sockaddr_storage& address, uint3
// copy message
messageLength = _fneHeader.getMessageLength();
- __UNIQUE_UINT8_ARRAY(message, messageLength);
+ UInt8Array message = std::unique_ptr(new uint8_t[messageLength]);
::memcpy(message.get(), buffer + (RTP_HEADER_LENGTH_BYTES + RTP_EXTENSION_HEADER_LENGTH_BYTES + RTP_FNE_HEADER_LENGTH_BYTES), messageLength);
uint16_t calc = edac::CRC::createCRC16(message.get(), messageLength * 8U);
diff --git a/src/common/network/RawFrameQueue.cpp b/src/common/network/RawFrameQueue.cpp
index 45993292..14e62782 100644
--- a/src/common/network/RawFrameQueue.cpp
+++ b/src/common/network/RawFrameQueue.cpp
@@ -87,7 +87,7 @@ UInt8Array RawFrameQueue::read(int& messageLength, sockaddr_storage& address, ui
// copy message
messageLength = length;
- __UNIQUE_UINT8_ARRAY(message, length);
+ UInt8Array message = std::unique_ptr(new uint8_t[length]);
::memcpy(message.get(), buffer, length);
return message;
diff --git a/src/common/network/UDPSocket.cpp b/src/common/network/UDPSocket.cpp
index 343f6499..fbc0d52e 100644
--- a/src/common/network/UDPSocket.cpp
+++ b/src/common/network/UDPSocket.cpp
@@ -57,8 +57,13 @@ UDPSocket::UDPSocket(const std::string& address, uint16_t port) :
m_address_save(address),
m_port_save(port),
m_isOpen(false),
+ m_aes(nullptr),
+ m_isCryptoWrapped(false),
+ m_presharedKey(nullptr),
m_counter(0U)
{
+ m_aes = new crypto::AES(crypto::AESKeyLength::AES_256);
+ m_presharedKey = new uint8_t[AES_WRAPPED_PCKT_KEY_LEN];
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
m_address[i] = "";
m_port[i] = 0U;
@@ -75,8 +80,13 @@ UDPSocket::UDPSocket(uint16_t port) :
m_address_save(),
m_port_save(port),
m_isOpen(false),
+ m_aes(nullptr),
+ m_isCryptoWrapped(false),
+ m_presharedKey(nullptr),
m_counter(0U)
{
+ m_aes = new crypto::AES(crypto::AESKeyLength::AES_256);
+ m_presharedKey = new uint8_t[AES_WRAPPED_PCKT_KEY_LEN];
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
m_address[i] = "";
m_port[i] = 0U;
@@ -90,7 +100,10 @@ UDPSocket::UDPSocket(uint16_t port) :
///
UDPSocket::~UDPSocket()
{
- /* stub */
+ if (m_aes != nullptr)
+ delete m_aes;
+ if (m_presharedKey != nullptr)
+ delete[] m_presharedKey;
}
///
@@ -233,6 +246,36 @@ int UDPSocket::read(uint8_t* buffer, uint32_t length, sockaddr_storage& address,
return -1;
}
+ // are we crypto wrapped?
+ if (m_isCryptoWrapped) {
+ // does the network packet contain the appropriate magic leader?
+ uint16_t magic = __GET_UINT16B(buffer, 0U);
+ if (magic == AES_WRAPPED_PCKT_MAGIC) {
+ uint32_t cryptedLen = (len - 2U) * sizeof(uint8_t);
+ // Utils::dump(1U, "UDPSocket::read() crypted", buffer + 2U, cryptedLen);
+
+ // decrypt
+ uint8_t* decrypted = m_aes->decryptECB(buffer + 2U, cryptedLen, m_presharedKey);
+
+ // Utils::dump(1U, "UDPSocket::read() decrypted", decrypted, cryptedLen);
+
+ // finalize, cleanup buffers and replace with new
+ if (decrypted != nullptr) {
+ ::memset(buffer, 0x00U, len);
+ ::memcpy(buffer, decrypted, len - 2U);
+
+ delete[] decrypted;
+ len -= 2U;
+ } else {
+ delete[] decrypted;
+ return 0;
+ }
+ }
+ else {
+ return 0; // this will effectively discard packets without the packet magic
+ }
+ }
+
m_counter++;
addrLen = size;
return len;
@@ -254,11 +297,54 @@ bool UDPSocket::write(const uint8_t* buffer, uint32_t length, const sockaddr_sto
bool result = false;
+ UInt8Array out = nullptr;
+ // are we crypto wrapped?
+ if (m_isCryptoWrapped) {
+ uint32_t cryptedLen = length * sizeof(uint8_t);
+ uint8_t* cryptoBuffer = new uint8_t[length];
+ ::memcpy(cryptoBuffer, buffer, length);
+
+ // do we need to pad the original buffer to be block aligned?
+ if (cryptedLen % crypto::AES::BLOCK_BYTES_LEN != 0) {
+ uint32_t alignment = crypto::AES::BLOCK_BYTES_LEN - (cryptedLen % crypto::AES::BLOCK_BYTES_LEN);
+ cryptedLen += alignment;
+
+ // reallocate buffer and copy
+ cryptoBuffer = new uint8_t[cryptedLen];
+ ::memset(cryptoBuffer, 0x00U, cryptedLen);
+ ::memcpy(cryptoBuffer, buffer, length);
+ }
+
+ // encrypt
+ uint8_t* crypted = m_aes->encryptECB(cryptoBuffer, cryptedLen, m_presharedKey);
+
+ // Utils::dump(1U, "UDPSocket::write() crypted", crypted, cryptedLen);
+
+ // finalize, cleanup buffers and replace with new
+ out = std::unique_ptr(new uint8_t[cryptedLen + 2U]);
+ delete[] cryptoBuffer;
+ if (crypted != nullptr) {
+ ::memcpy(out.get() + 2U, crypted, cryptedLen);
+ __SET_UINT16B(AES_WRAPPED_PCKT_MAGIC, out.get(), 0U);
+ delete[] crypted;
+ } else {
+ if (lenWritten != nullptr) {
+ *lenWritten = -1;
+ }
+
+ delete[] crypted;
+ return false;
+ }
+ } else {
+ out = std::unique_ptr(new uint8_t[length]);
+ ::memcpy(out.get(), buffer, length);
+ }
+
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
if (m_fd[i] < 0 || m_af[i] != address.ss_family)
continue;
- ssize_t sent = ::sendto(m_fd[i], (char*)buffer, length, 0, (sockaddr*)& address, addrLen);
+ ssize_t sent = ::sendto(m_fd[i], (char*)out.get(), length, 0, (sockaddr*)& address, addrLen);
if (sent < 0) {
LogError(LOG_NET, "Error returned from sendto, err: %d", errno);
@@ -321,6 +407,53 @@ bool UDPSocket::write(BufferVector& buffers, int* lenWritten)
continue;
}
+ uint32_t length = buffers.at(i)->length;
+ if (buffers.at(i)->buffer == nullptr) {
+ LogError(LOG_NET, "discarding buffered message with len = %u, but deleted buffer?", length);
+ --size;
+ continue;
+ }
+
+ // are we crypto wrapped?
+ if (m_isCryptoWrapped) {
+ uint32_t cryptedLen = length * sizeof(uint8_t);
+ uint8_t* cryptoBuffer = buffers.at(i)->buffer;
+
+ // do we need to pad the original buffer to be block aligned?
+ if (cryptedLen % crypto::AES::BLOCK_BYTES_LEN != 0) {
+ uint32_t alignment = crypto::AES::BLOCK_BYTES_LEN - (cryptedLen % crypto::AES::BLOCK_BYTES_LEN);
+ cryptedLen += alignment;
+
+ // reallocate buffer and copy
+ cryptoBuffer = new uint8_t[cryptedLen];
+ ::memset(cryptoBuffer, 0x00U, cryptedLen);
+ ::memcpy(cryptoBuffer, buffers.at(i)->buffer, length);
+ }
+
+ // encrypt
+ uint8_t* crypted = m_aes->encryptECB(cryptoBuffer, cryptedLen, m_presharedKey);
+ delete[] cryptoBuffer;
+
+ if (crypted == nullptr) {
+ --size;
+ continue;
+ }
+
+ // Utils::dump(1U, "UDPSocket::write() crypted", crypted, cryptedLen);
+
+ // finalize
+ uint8_t out[cryptedLen + 2U];
+ ::memcpy(out + 2U, crypted, cryptedLen);
+ __SET_UINT16B(AES_WRAPPED_PCKT_MAGIC, out, 0U);
+
+ // cleanup buffers and replace with new
+ delete[] crypted;
+ //delete buffers[i]->buffer;
+ buffers[i]->buffer = new uint8_t[cryptedLen + 2U];
+ ::memcpy(buffers[i]->buffer, out, cryptedLen + 2U);
+ buffers[i]->length = cryptedLen + 2U;
+ }
+
chunks[i].iov_len = buffers.at(i)->length;
chunks[i].iov_base = buffers.at(i)->buffer;
sent += buffers.at(i)->length;
@@ -393,6 +526,22 @@ void UDPSocket::close(const uint32_t index)
}
}
+///
+/// Sets the preshared encryption key.
+///
+///
+void UDPSocket::setPresharedKey(const uint8_t* presharedKey)
+{
+ if (presharedKey != nullptr) {
+ ::memset(m_presharedKey, 0x00U, AES_WRAPPED_PCKT_KEY_LEN);
+ ::memcpy(m_presharedKey, presharedKey, AES_WRAPPED_PCKT_KEY_LEN);
+ m_isCryptoWrapped = true;
+ } else {
+ ::memset(m_presharedKey, 0x00U, AES_WRAPPED_PCKT_KEY_LEN);
+ m_isCryptoWrapped = false;
+ }
+}
+
///
/// Helper to lookup a hostname and resolve it to an IP address.
///
diff --git a/src/common/network/UDPSocket.h b/src/common/network/UDPSocket.h
index 7cdcf856..4a1224e6 100644
--- a/src/common/network/UDPSocket.h
+++ b/src/common/network/UDPSocket.h
@@ -32,6 +32,7 @@
#define __UDP_SOCKET_H__
#include "common/Defines.h"
+#include "common/AESCrypto.h"
#include
#include
@@ -50,6 +51,9 @@
#define UDP_SOCKET_MAX 1
#endif
+#define AES_WRAPPED_PCKT_MAGIC 0xC0FEU
+#define AES_WRAPPED_PCKT_KEY_LEN 32
+
enum IPMATCHTYPE {
IMT_ADDRESS_AND_PORT,
IMT_ADDRESS_ONLY
@@ -130,6 +134,9 @@ namespace network
/// Closes the UDP socket connection.
void close(const uint32_t index);
+ /// Sets the preshared encryption key.
+ void setPresharedKey(const uint8_t* presharedKey);
+
/// Flag indicating the UDP socket(s) are open.
bool isOpen() const { return m_isOpen; }
@@ -159,6 +166,10 @@ namespace network
uint32_t m_af[UDP_SOCKET_MAX];
int m_fd[UDP_SOCKET_MAX];
+ crypto::AES* m_aes;
+ bool m_isCryptoWrapped;
+ uint8_t* m_presharedKey;
+
uint32_t m_counter;
};
} // namespace network
diff --git a/src/common/p25/lc/TDULC.cpp b/src/common/p25/lc/TDULC.cpp
index 70ab6b98..e59192c1 100644
--- a/src/common/p25/lc/TDULC.cpp
+++ b/src/common/p25/lc/TDULC.cpp
@@ -150,7 +150,8 @@ ulong64_t TDULC::toValue(const uint8_t* payload)
///
UInt8Array TDULC::fromValue(const ulong64_t value)
{
- __UNIQUE_UINT8_ARRAY(payload, P25_TDULC_PAYLOAD_LENGTH_BYTES);
+ UInt8Array payload = std::unique_ptr(new uint8_t[P25_TDULC_PAYLOAD_LENGTH_BYTES]);
+ ::memset(payload.get(), 0x00U, P25_TDULC_PAYLOAD_LENGTH_BYTES);
// split ulong64_t (8 byte) value into bytes
payload[0U] = (uint8_t)((value >> 56) & 0xFFU);
diff --git a/src/common/p25/lc/TSBK.cpp b/src/common/p25/lc/TSBK.cpp
index 8596745a..f4125f24 100644
--- a/src/common/p25/lc/TSBK.cpp
+++ b/src/common/p25/lc/TSBK.cpp
@@ -195,7 +195,8 @@ ulong64_t TSBK::toValue(const uint8_t* payload)
///
UInt8Array TSBK::fromValue(const ulong64_t value)
{
- __UNIQUE_UINT8_ARRAY(payload, P25_TSBK_LENGTH_BYTES - 4U);
+ UInt8Array payload = std::unique_ptr(new uint8_t[P25_TSBK_LENGTH_BYTES - 4U]);
+ ::memset(payload.get(), 0x00U, P25_TSBK_LENGTH_BYTES - 4U);
// split ulong64_t (8 byte) value into bytes
payload[0U] = (uint8_t)((value >> 56) & 0xFFU);
diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp
index f2673119..cfb1dc60 100644
--- a/src/fne/HostFNE.cpp
+++ b/src/fne/HostFNE.cpp
@@ -395,6 +395,38 @@ bool HostFNE::createMasterNetwork()
bool verbose = masterConf["verbose"].as(false);
bool debug = masterConf["debug"].as(false);
+ bool encrypted = masterConf["encrypted"].as(false);
+ std::string key = masterConf["presharedKey"].as();
+ uint8_t presharedKey[AES_WRAPPED_PCKT_KEY_LEN];
+ if (!key.empty()) {
+ if (key.size() == 32) {
+ // bryanb: shhhhhhh....dirty nasty hacks
+ key = key.append(key); // since the key is 32 characters (16 hex pairs), double it on itself for 64 characters (32 hex pairs)
+ LogWarning(LOG_HOST, "Half-length master network preshared encryption key detected, doubling key on itself.");
+ }
+
+ if (key.size() == 64) {
+ if ((key.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos)) {
+ const char* keyPtr = key.c_str();
+ ::memset(presharedKey, 0x00U, AES_WRAPPED_PCKT_KEY_LEN);
+
+ for (uint8_t i = 0; i < AES_WRAPPED_PCKT_KEY_LEN; i++) {
+ char t[4] = {keyPtr[0], keyPtr[1], 0};
+ presharedKey[i] = (uint8_t)::strtoul(t, NULL, 16);
+ keyPtr += 2 * sizeof(char);
+ }
+ }
+ else {
+ LogWarning(LOG_HOST, "Invalid characters in the master network preshared encryption key. Encryption disabled.");
+ encrypted = false;
+ }
+ }
+ else {
+ LogWarning(LOG_HOST, "Invalid master network preshared encryption key length, key should be 32 hex pairs, or 64 characters. Encryption disabled.");
+ encrypted = false;
+ }
+ }
+
if (id > 999999999U) {
::LogError(LOG_HOST, "Network Peer ID cannot be greater then 999999999.");
return false;
@@ -421,6 +453,8 @@ bool HostFNE::createMasterNetwork()
LogInfo(" Parrot Repeat Delay: %u ms", parrotDelay);
LogInfo(" Parrot Grant Demand: %s", parrotGrantDemand ? "yes" : "no");
+ LogInfo(" Encrypted: %s", encrypted ? "yes" : "no");
+
if (verbose) {
LogInfo(" Verbose: yes");
}
@@ -447,6 +481,10 @@ bool HostFNE::createMasterNetwork()
return false;
}
+ if (encrypted) {
+ m_network->setPresharedKey(presharedKey);
+ }
+
return true;
}
@@ -468,6 +506,38 @@ bool HostFNE::createPeerNetworks()
uint32_t id = peerConf["peerId"].as(1001U);
bool debug = peerConf["debug"].as(false);
+ bool encrypted = peerConf["encrypted"].as(false);
+ std::string key = peerConf["presharedKey"].as();
+ uint8_t presharedKey[AES_WRAPPED_PCKT_KEY_LEN];
+ if (!key.empty()) {
+ if (key.size() == 32) {
+ // bryanb: shhhhhhh....dirty nasty hacks
+ key = key.append(key); // since the key is 32 characters (16 hex pairs), double it on itself for 64 characters (32 hex pairs)
+ LogWarning(LOG_HOST, "Half-length peer network preshared encryption key detected, doubling key on itself.");
+ }
+
+ if (key.size() == 64) {
+ if ((key.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos)) {
+ const char* keyPtr = key.c_str();
+ ::memset(presharedKey, 0x00U, AES_WRAPPED_PCKT_KEY_LEN);
+
+ for (uint8_t i = 0; i < AES_WRAPPED_PCKT_KEY_LEN; i++) {
+ char t[4] = {keyPtr[0], keyPtr[1], 0};
+ presharedKey[i] = (uint8_t)::strtoul(t, NULL, 16);
+ keyPtr += 2 * sizeof(char);
+ }
+ }
+ else {
+ LogWarning(LOG_HOST, "Invalid characters in the peer network preshared encryption key. Encryption disabled.");
+ encrypted = false;
+ }
+ }
+ else {
+ LogWarning(LOG_HOST, "Invalid peer network preshared encryption key length, key should be 32 hex pairs, or 64 characters. Encryption disabled.");
+ encrypted = false;
+ }
+ }
+
std::string identity = peerConf["identity"].as();
uint32_t rxFrequency = peerConf["rxFrequency"].as(0U);
uint32_t txFrequency = peerConf["txFrequency"].as(0U);
@@ -475,7 +545,7 @@ bool HostFNE::createPeerNetworks()
float longitude = peerConf["longitude"].as(0.0F);
std::string location = peerConf["location"].as();
- ::LogInfoEx(LOG_HOST, "Peer ID %u Master Address %s Master Port %u Identity %s Enabled %u", id, masterAddress.c_str(), masterPort, identity.c_str(), enabled);
+ ::LogInfoEx(LOG_HOST, "Peer ID %u Master Address %s Master Port %u Identity %s Enabled %u Encrypted %u", id, masterAddress.c_str(), masterPort, identity.c_str(), enabled, encrypted);
if (id > 999999999U) {
::LogError(LOG_HOST, "Network Peer ID cannot be greater then 999999999.");
@@ -485,6 +555,9 @@ bool HostFNE::createPeerNetworks()
// initialize networking
network::PeerNetwork* network = new PeerNetwork(masterAddress, masterPort, 0U, id, password, true, debug, m_dmrEnabled, m_p25Enabled, m_nxdnEnabled, true, true, m_allowActivityTransfer, m_allowDiagnosticTransfer, false);
network->setMetadata(identity, rxFrequency, txFrequency, 0.0F, 0.0F, 0, 0, 0, latitude, longitude, 0, location);
+ if (encrypted) {
+ m_network->setPresharedKey(presharedKey);
+ }
network->enable(enabled);
if (enabled) {
diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp
index 224855da..c5f72d0f 100644
--- a/src/fne/network/FNENetwork.cpp
+++ b/src/fne/network/FNENetwork.cpp
@@ -124,6 +124,14 @@ void FNENetwork::setLookups(lookups::RadioIdLookup* ridLookup, lookups::Talkgrou
m_tidLookup = tidLookup;
}
+///
+/// Sets endpoint preshared encryption key.
+///
+void FNENetwork::setPresharedKey(const uint8_t* presharedKey)
+{
+ m_socket->setPresharedKey(presharedKey);
+}
+
///
/// Updates the timer by the passed number of milliseconds.
///
diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h
index 1a1996ce..ff8389d4 100644
--- a/src/fne/network/FNENetwork.h
+++ b/src/fne/network/FNENetwork.h
@@ -170,6 +170,8 @@ namespace network
/// Sets the instances of the Radio ID and Talkgroup Rules lookup tables.
void setLookups(lookups::RadioIdLookup* ridLookup, lookups::TalkgroupRulesLookup* tidLookup);
+ /// Sets endpoint preshared encryption key.
+ void setPresharedKey(const uint8_t* presharedKey);
/// Updates the timer by the passed number of milliseconds.
void clock(uint32_t ms);
diff --git a/src/host/Host.Config.cpp b/src/host/Host.Config.cpp
index b9c3452d..6b60271f 100644
--- a/src/host/Host.Config.cpp
+++ b/src/host/Host.Config.cpp
@@ -686,6 +686,38 @@ bool Host::createNetwork()
bool updateLookup = networkConf["updateLookups"].as(false);
bool debug = networkConf["debug"].as(false);
+ bool encrypted = networkConf["encrypted"].as(false);
+ std::string key = networkConf["presharedKey"].as();
+ uint8_t presharedKey[AES_WRAPPED_PCKT_KEY_LEN];
+ if (!key.empty()) {
+ if (key.size() == 32) {
+ // bryanb: shhhhhhh....dirty nasty hacks
+ key = key.append(key); // since the key is 32 characters (16 hex pairs), double it on itself for 64 characters (32 hex pairs)
+ LogWarning(LOG_HOST, "Half-length network preshared encryption key detected, doubling key on itself.");
+ }
+
+ if (key.size() == 64) {
+ if ((key.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos)) {
+ const char* keyPtr = key.c_str();
+ ::memset(presharedKey, 0x00U, AES_WRAPPED_PCKT_KEY_LEN);
+
+ for (uint8_t i = 0; i < AES_WRAPPED_PCKT_KEY_LEN; i++) {
+ char t[4] = {keyPtr[0], keyPtr[1], 0};
+ presharedKey[i] = (uint8_t)::strtoul(t, NULL, 16);
+ keyPtr += 2 * sizeof(char);
+ }
+ }
+ else {
+ LogWarning(LOG_HOST, "Invalid characters in the network preshared encryption key. Encryption disabled.");
+ encrypted = false;
+ }
+ }
+ else {
+ LogWarning(LOG_HOST, "Invalid network preshared encryption key length, key should be 32 hex pairs, or 64 characters. Encryption disabled.");
+ encrypted = false;
+ }
+ }
+
if (id > 999999999U) {
::LogError(LOG_HOST, "Network Peer ID cannot be greater then 999999999.");
return false;
@@ -722,6 +754,8 @@ bool Host::createNetwork()
LogInfo(" Allow Diagnostic Log Transfer: %s", allowDiagnosticTransfer ? "yes" : "no");
LogInfo(" Update Lookups: %s", updateLookup ? "yes" : "no");
+ LogInfo(" Encrypted: %s", encrypted ? "yes" : "no");
+
if (debug) {
LogInfo(" Debug: yes");
}
@@ -746,6 +780,9 @@ bool Host::createNetwork()
if (restApiEnable) {
m_network->setRESTAPIData(restApiPassword, restApiPort);
}
+ if (encrypted) {
+ m_network->setPresharedKey(presharedKey);
+ }
m_network->enable(true);
bool ret = m_network->open();
diff --git a/src/host/network/Network.cpp b/src/host/network/Network.cpp
index b91f9546..3a875efd 100644
--- a/src/host/network/Network.cpp
+++ b/src/host/network/Network.cpp
@@ -214,6 +214,14 @@ void Network::setRESTAPIData(const std::string& password, uint16_t port)
m_restApiPort = port;
}
+///
+/// Sets endpoint preshared encryption key.
+///
+void Network::setPresharedKey(const uint8_t* presharedKey)
+{
+ m_socket->setPresharedKey(presharedKey);
+}
+
///
/// Updates the timer by the passed number of milliseconds.
///
diff --git a/src/host/network/Network.h b/src/host/network/Network.h
index 40144781..fc38ed2a 100644
--- a/src/host/network/Network.h
+++ b/src/host/network/Network.h
@@ -68,6 +68,8 @@ namespace network
uint8_t channelId, uint32_t channelNo, uint32_t power, float latitude, float longitude, int height, const std::string& location);
/// Sets REST API configuration settings from the modem.
void setRESTAPIData(const std::string& password, uint16_t port);
+ /// Sets endpoint preshared encryption key.
+ void setPresharedKey(const uint8_t* presharedKey);
/// Updates the timer by the passed number of milliseconds.
void clock(uint32_t ms);
diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp
index b9481633..b9007bd4 100644
--- a/src/host/p25/Control.cpp
+++ b/src/host/p25/Control.cpp
@@ -1703,5 +1703,7 @@ void Control::generateLLA_AM1_Parameters()
LogMessage(LOG_P25, "P25, generated LLA AM1 parameters");
}
+ // cleanup
+ delete[] KS;
delete aes;
}
diff --git a/src/host/p25/packet/ControlSignaling.cpp b/src/host/p25/packet/ControlSignaling.cpp
index dc6703d3..19940134 100644
--- a/src/host/p25/packet/ControlSignaling.cpp
+++ b/src/host/p25/packet/ControlSignaling.cpp
@@ -639,6 +639,9 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptrgetSysId());
}
diff --git a/tests/crypto/AES_Crypto_Test.cpp b/tests/crypto/AES_Crypto_Test.cpp
new file mode 100644
index 00000000..09fa5898
--- /dev/null
+++ b/tests/crypto/AES_Crypto_Test.cpp
@@ -0,0 +1,83 @@
+/**
+* Digital Voice Modem - Host Software (Test Suite)
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Host Software / Test Suite
+*
+*/
+/*
+* Copyright (C) 2023 Bryan Biedenkapp N2PLL
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include "host/Defines.h"
+#include "common/AESCrypto.h"
+#include "common/Log.h"
+#include "common/Utils.h"
+
+using namespace crypto;
+
+#include
+#include
+#include
+
+TEST_CASE("AES", "[Crypto Test]") {
+ SECTION("AES_Crypto_Test") {
+ bool failed = false;
+
+ INFO("AES Crypto Test");
+
+ srand((unsigned int)time(NULL));
+
+ // key (K)
+ uint8_t K[32] =
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
+ };
+
+ // message
+ uint8_t message[48] =
+ {
+ 0x90, 0x56, 0x00, 0x00, 0x2D, 0x75, 0xE6, 0x8D, 0x00, 0x89, 0x69, 0xCF, 0x00, 0xFE, 0x00, 0x04,
+ 0x4F, 0xC7, 0x60, 0xFF, 0x30, 0x3E, 0x2B, 0xAD, 0x00, 0x89, 0x69, 0xCF, 0x00, 0x00, 0x00, 0x08,
+ 0x52, 0x50, 0x54, 0x4C, 0x00, 0x89, 0x69, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ // perform crypto
+ AES* aes = new AES(AESKeyLength::AES_256);
+
+ Utils::dump(2U, "AES_Crypto_Test, Message", message, 48);
+
+ uint8_t* crypted = aes->encryptECB(message, 48 * sizeof(uint8_t), K);
+ Utils::dump(2U, "AES_Crypto_Test, Encrypted", crypted, 48);
+
+ uint8_t* decrypted = aes->decryptECB(crypted, 48 * sizeof(uint8_t), K);
+ Utils::dump(2U, "AES_Crypto_Test, Decrypted", decrypted, 48);
+
+ for (uint32_t i = 0; i < 48U; i++) {
+ if (decrypted[i] != message[i]) {
+ ::LogDebug("T", "AES_Crypto_Test, INVALID AT IDX %d\n", i);
+ failed = true;
+ }
+ }
+
+ delete aes;
+ REQUIRE(failed==false);
+ }
+}