very very preliminary work for U_REG LLA support;

3.5-maint
Bryan Biedenkapp 2 years ago
parent 5ea4ccc98e
commit 936402275f

@ -206,6 +206,8 @@ protocols:
verifyAff: false
# Flag indicating the host should verify unit registration.
verifyReg: false
# Flag indicating the host requires LLA verification before allowing unit registration.
requireLLAForReg: false
# Flag indicating whether verbose dumping of P25 data packets is enabled.
dumpDataPacket: false
# Flag indicating whether or not this host will repeat P25 data traffic.
@ -376,6 +378,12 @@ system:
# REST API access password for voice channel.
restPassword: "PASSWORD"
secure:
# AES-128 16-byte Key (used for LLA)
# (This field *must* be 16 hex bytes in length or 32 characters
# 0 - 9, A - F.)
key: "000102030405060708090A0B0C0D0E0F"
# DMR Color Code.
colorCode: 1
# P25 Network Access Code (NAC). (Rx/Tx)

@ -36,6 +36,7 @@
#include "p25/Sync.h"
#include "edac/CRC.h"
#include "remote/RESTClient.h"
#include "AESCrypto.h"
#include "HostMain.h"
#include "Log.h"
#include "Utils.h"
@ -137,6 +138,11 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q
m_frameLossThreshold(DEFAULT_FRAME_LOSS_THRESHOLD),
m_ccFrameCnt(0U),
m_ccSeq(0U),
m_random(),
m_llaK(nullptr),
m_llaRS(nullptr),
m_llaCRS(nullptr),
m_llaKS(nullptr),
m_nid(nac),
m_siteData(),
m_rssiMapper(rssiMapper),
@ -161,6 +167,15 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q
m_voice = new Voice(this, debug, verbose);
m_control = new ControlSignaling(this, dumpTSBKData, debug, verbose);
m_data = new Data(this, dumpPDUData, repeatPDU, debug, verbose);
std::random_device rd;
std::mt19937 mt(rd());
m_random = mt;
m_llaK = nullptr;
m_llaRS = new uint8_t[P25_AUTH_KEY_LENGTH_BYTES];
m_llaCRS = new uint8_t[P25_AUTH_KEY_LENGTH_BYTES];
m_llaKS = new uint8_t[P25_AUTH_KEY_LENGTH_BYTES];
}
/// <summary>
@ -233,6 +248,34 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
m_control->m_patchSuperGroup = (uint32_t)::strtoul(rfssConfig["pSuperGroup"].as<std::string>("FFFE").c_str(), NULL, 16);
m_control->m_announcementGroup = (uint32_t)::strtoul(rfssConfig["announcementGroup"].as<std::string>("FFFE").c_str(), NULL, 16);
yaml::Node secureConfig = rfssConfig["secure"];
std::string key = secureConfig["key"].as<std::string>();
if (!key.empty()) {
if (key.size() == 32) {
if ((key.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos)) {
const char* keyPtr = key.c_str();
m_llaK = new uint8_t[P25_AUTH_KEY_LENGTH_BYTES];
::memset(m_llaK, 0x00U, P25_AUTH_KEY_LENGTH_BYTES);
for (uint8_t i = 0; i < P25_AUTH_KEY_LENGTH_BYTES; i++) {
char t[4] = {keyPtr[0], keyPtr[1], 0};
m_llaK[i] = (uint8_t)::strtoul(t, NULL, 16);
keyPtr += 2 * sizeof(char);
}
}
else {
LogWarning(LOG_P25, "Invalid characters in the secure key. LLA disabled.");
}
}
else {
LogWarning(LOG_P25, "Invalid secure key length, key should be 16 hex pairs, or 32 characters. LLA disabled.");
}
}
if (m_llaK != nullptr) {
generateLLA_AM1_Parameters();
}
m_inhibitUnauth = p25Protocol["inhibitUnauthorized"].as<bool>(false);
m_legacyGroupGrnt = p25Protocol["legacyGroupGrnt"].as<bool>(true);
m_legacyGroupReg = p25Protocol["legacyGroupReg"].as<bool>(false);
@ -240,6 +283,12 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
m_control->m_verifyAff = p25Protocol["verifyAff"].as<bool>(false);
m_control->m_verifyReg = p25Protocol["verifyReg"].as<bool>(false);
m_control->m_requireLLAForReg = p25Protocol["requireLLAForReg"].as<bool>(false);
if (m_llaK == nullptr) {
m_control->m_requireLLAForReg = false;
}
m_control->m_noStatusAck = p25Protocol["noStatusAck"].as<bool>(false);
m_control->m_noMessageAck = p25Protocol["noMessageAck"].as<bool>(true);
m_control->m_unitToUnitAvailCheck = p25Protocol["unitToUnitAvailCheck"].as<bool>(true);
@ -433,11 +482,16 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
}
LogInfo(" Time/Date Announcement TSBK: %s", m_control->m_ctrlTimeDateAnn ? "yes" : "no");
if (m_llaK != nullptr) {
LogInfo(" Link Layer Authentication: yes");
}
LogInfo(" Inhibit Unauthorized: %s", m_inhibitUnauth ? "yes" : "no");
LogInfo(" Legacy Group Grant: %s", m_legacyGroupGrnt ? "yes" : "no");
LogInfo(" Legacy Group Registration: %s", m_legacyGroupReg ? "yes" : "no");
LogInfo(" Verify Affiliation: %s", m_control->m_verifyAff ? "yes" : "no");
LogInfo(" Verify Registration: %s", m_control->m_verifyReg ? "yes" : "no");
LogInfo(" Require LLA for Registration: %s", m_control->m_requireLLAForReg ? "yes" : "no");
LogInfo(" SNDCP Channel Grant: %s", m_control->m_sndcpChGrant ? "yes" : "no");
@ -1582,3 +1636,49 @@ void Control::writeRF_TDU(bool noNetwork)
addFrame(data, P25_TDU_FRAME_LENGTH_BYTES + 2U);
}
}
/// <summary>
/// Helper to setup and generate LLA AM1 parameters.
/// </summary>
void Control::generateLLA_AM1_Parameters()
{
::memset(m_llaRS, 0x00U, P25_AUTH_KEY_LENGTH_BYTES);
::memset(m_llaCRS, 0x00U, P25_AUTH_KEY_LENGTH_BYTES);
::memset(m_llaKS, 0x00U, P25_AUTH_KEY_LENGTH_BYTES);
if (m_llaK == nullptr) {
return;
}
crypto::AES* aes = new crypto::AES(crypto::AESKeyLength::AES_128);
// generate new RS
uint8_t RS[P25_AUTH_RAND_SEED_LENGTH_BYTES];
std::uniform_int_distribution<uint32_t> dist(DVM_RAND_MIN, DVM_RAND_MAX);
uint32_t rnd = dist(m_random);
__SET_UINT32(rnd, RS, 0U);
rnd = dist(m_random);
__SET_UINT32(rnd, RS, 4U);
rnd = dist(m_random);
RS[9U] = (uint8_t)(rnd & 0xFFU);
// expand RS to 16 bytes
for (uint32_t i = 0; i < P25_AUTH_RAND_SEED_LENGTH_BYTES; i++)
m_llaRS[i] = RS[i];
// complement RS
for (uint32_t i = 0; i < P25_AUTH_KEY_LENGTH_BYTES; i++)
m_llaCRS[i] = ~m_llaRS[i];
// perform crypto
uint8_t* KS = aes->encryptECB(m_llaRS, P25_AUTH_KEY_LENGTH_BYTES * sizeof(uint8_t), m_llaK);
::memcpy(m_llaKS, KS, P25_AUTH_KEY_LENGTH_BYTES);
if (m_verbose) {
LogMessage(LOG_P25, "P25, generated LLA AM1 parameters");
}
delete aes;
}

@ -51,6 +51,7 @@
#include <cstdio>
#include <vector>
#include <unordered_map>
#include <random>
namespace p25
{
@ -220,6 +221,13 @@ namespace p25
uint8_t m_ccFrameCnt;
uint8_t m_ccSeq;
std::mt19937 m_random;
uint8_t* m_llaK;
uint8_t* m_llaRS;
uint8_t* m_llaCRS;
uint8_t* m_llaKS;
NID m_nid;
SiteData m_siteData;
@ -260,6 +268,9 @@ namespace p25
void writeRF_Preamble(uint32_t preambleCount = 0, bool force = false);
/// <summary>Helper to write a P25 TDU packet.</summary>
void writeRF_TDU(bool noNetwork);
/// <summary>Helper to setup and generate LLA AM1 parameters.</summary>
void generateLLA_AM1_Parameters();
};
} // namespace p25

@ -112,6 +112,7 @@ namespace p25
const uint8_t P25_AUTH_RES_LENGTH_BYTES = 4U;
const uint8_t P25_AUTH_RAND_SEED_LENGTH_BYTES = 10U;
const uint8_t P25_AUTH_RAND_CHLNG_LENGTH_BYTES = 5U;
const uint8_t P25_AUTH_KEY_LENGTH_BYTES = 16U;
const uint8_t P25_ALGO_UNENCRYPT = 0x80U;
@ -170,6 +171,7 @@ namespace p25
const uint32_t P25_DENY_RSN_REQ_UNIT_NOT_AUTH = 0x11U;
const uint32_t P25_DENY_RSN_TGT_UNIT_NOT_VALID = 0x20U;
const uint32_t P25_DENY_RSN_TGT_UNIT_NOT_AUTH = 0x21U;
const uint32_t P25_DENY_RSN_SU_FAILED_AUTH = 0x22U;
const uint32_t P25_DENY_RSN_TGT_UNIT_REFUSED = 0x2FU;
const uint32_t P25_DENY_RSN_TGT_GROUP_NOT_VALID = 0x30U;
const uint32_t P25_DENY_RSN_TGT_GROUP_NOT_AUTH = 0x31U;

@ -36,6 +36,7 @@
#include "p25/Sync.h"
#include "edac/CRC.h"
#include "remote/RESTClient.h"
#include "AESCrypto.h"
#include "HostMain.h"
#include "Log.h"
#include "Thread.h"
@ -50,6 +51,7 @@ using namespace p25::packet;
#include <cstdio>
#include <cstring>
#include <ctime>
#include <random>
// ---------------------------------------------------------------------------
// Macros
@ -575,7 +577,12 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr<lc::
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_U_REG, true, true);
}
writeRF_TSDU_U_Reg_Rsp(srcId, tsbk->getSysId());
if (m_requireLLAForReg) {
writeRF_TSDU_Auth_Dmd(srcId);
}
else {
writeRF_TSDU_U_Reg_Rsp(srcId, tsbk->getSysId());
}
}
break;
case TSBK_ISP_LOC_REG_REQ:
@ -587,6 +594,66 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len, std::unique_ptr<lc::
writeRF_TSDU_Loc_Reg_Rsp(srcId, dstId, tsbk->getGroup());
}
break;
case TSBK_ISP_AUTH_RESP:
{
// make sure control data is supported
IS_SUPPORT_CONTROL_CHECK(tsbk->toString(true), TSBK_ISP_AUTH_RESP, srcId);
ISP_AUTH_RESP* isp = static_cast<ISP_AUTH_RESP*>(tsbk.get());
if (m_verbose) {
LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u",
tsbk->toString(true).c_str(), srcId);
}
::ActivityLog("P25", true, "authentication response from %u", srcId);
crypto::AES* aes = new crypto::AES(crypto::AESKeyLength::AES_128);
// get RES1 from response
uint8_t RES1[P25_AUTH_RES_LENGTH_BYTES];
isp->getAuthRes(RES1);
// get challenge for our SU
ulong64_t challenge = 0U;
try {
challenge = m_llaDemandTable.at(srcId);
}
catch (...) {
challenge = 0U;
}
uint8_t RC[P25_AUTH_RAND_CHLNG_LENGTH_BYTES];
__SET_UINT32(challenge >> 8, RC, 0);
RC[4U] = (uint8_t)(challenge & 0xFFU);
// expand RAND1 to 16 bytes
uint8_t expandedRAND1[16];
::memset(expandedRAND1, 0x00U, P25_AUTH_KEY_LENGTH_BYTES);
for (uint32_t i = 0; i < P25_AUTH_RAND_CHLNG_LENGTH_BYTES; i++)
expandedRAND1[i] = RC[i];
// generate XRES1
uint8_t* XRES1 = aes->encryptECB(expandedRAND1, P25_AUTH_KEY_LENGTH_BYTES * sizeof(uint8_t), m_p25->m_llaKS);
// compare RES1 and XRES1
bool authFailed = false;
for (uint32_t i = 0; i < P25_AUTH_RES_LENGTH_BYTES; i++) {
if (XRES1[i] != RES1[i]) {
authFailed = true;
}
}
if (m_p25->m_ackTSBKRequests) {
writeRF_TSDU_ACK_FNE(srcId, TSBK_ISP_AUTH_RESP, true, true);
}
if (!authFailed) {
writeRF_TSDU_U_Reg_Rsp(srcId, tsbk->getSysId());
}
else {
writeRF_TSDU_Deny(P25_WUID_FNE, srcId, P25_DENY_RSN_SU_FAILED_AUTH, TSBK_IOSP_U_REG);
}
}
default:
LogError(LOG_RF, P25_TSDU_STR ", unhandled LCO, mfId = $%02X, lco = $%02X", tsbk->getMFId(), tsbk->getLCO());
break;
@ -1193,6 +1260,7 @@ ControlSignaling::ControlSignaling(Control* p25, bool dumpTSBKData, bool debug,
m_announcementGroup(0xFFFEU),
m_verifyAff(false),
m_verifyReg(false),
m_requireLLAForReg(false),
m_rfMBF(nullptr),
m_mbfCnt(0U),
m_mbfIdenCnt(0U),
@ -1203,6 +1271,7 @@ ControlSignaling::ControlSignaling(Control* p25, bool dumpTSBKData, bool debug,
m_adjSiteUpdateCnt(),
m_sccbTable(),
m_sccbUpdateCnt(),
m_llaDemandTable(),
m_lastMFID(P25_MFG_STANDARD),
m_noStatusAck(false),
m_noMessageAck(true),
@ -1231,6 +1300,8 @@ ControlSignaling::ControlSignaling(Control* p25, bool dumpTSBKData, bool debug,
m_sccbTable.clear();
m_sccbUpdateCnt.clear();
m_llaDemandTable.clear();
m_adjSiteUpdateInterval = ADJ_SITE_TIMER_TIMEOUT;
m_adjSiteUpdateTimer.setTimeout(m_adjSiteUpdateInterval);
m_adjSiteUpdateTimer.start();
@ -2786,6 +2857,39 @@ bool ControlSignaling::writeRF_TSDU_Loc_Reg_Rsp(uint32_t srcId, uint32_t dstId,
return ret;
}
/// <summary>
/// Helper to write a LLA demand.
/// </summary>
/// <param name="srcId"></param>
void ControlSignaling::writeRF_TSDU_Auth_Dmd(uint32_t srcId)
{
std::unique_ptr<MBT_OSP_AUTH_DMD> osp = new_unique(MBT_OSP_AUTH_DMD);
osp->setSrcId(srcId);
osp->setAuthRS(m_p25->m_llaRS);
// generate challenge
uint8_t RC[P25_AUTH_RAND_CHLNG_LENGTH_BYTES];
std::uniform_int_distribution<uint32_t> dist(DVM_RAND_MIN, DVM_RAND_MAX);
uint32_t rnd = dist(m_p25->m_random);
__SET_UINT32(rnd, RC, 0U);
rnd = dist(m_p25->m_random);
RC[4U] = (uint8_t)(rnd & 0xFFU);
ulong64_t challenge = __GET_UINT32(RC, 0U);
challenge = (challenge << 8) + RC[4U];
osp->setAuthRC(RC);
m_llaDemandTable[srcId] = challenge;
if (m_verbose) {
LogMessage(LOG_RF, P25_TSDU_STR ", %s, srcId = %u, RC = %X", osp->toString().c_str(), srcId, challenge);
}
writeRF_TSDU_AMBT(osp.get());
}
/// <summary>
/// Helper to write a call termination packet.
/// </summary>

@ -112,6 +112,7 @@ namespace p25
bool m_verifyAff;
bool m_verifyReg;
bool m_requireLLAForReg;
uint8_t* m_rfMBF;
uint8_t m_mbfCnt;
@ -127,6 +128,8 @@ namespace p25
std::unordered_map<uint8_t, SiteData> m_sccbTable;
std::unordered_map<uint8_t, uint8_t> m_sccbUpdateCnt;
std::unordered_map<uint32_t, ulong64_t> m_llaDemandTable;
uint8_t m_lastMFID;
bool m_noStatusAck;
@ -182,7 +185,7 @@ namespace p25
virtual void writeNet_TSDU(lc::TSBK* tsbk);
/// <summary>Helper to write a multi-block (3-block) P25 TSDU packet.</summary>
void writeRF_TSDU_MBF(lc::TSBK* tsbk, bool clearBeforeWrite = false);
/// <summary>Helper to write a alternate multi-block ControlSignalinging PDU packet.</summary>
/// <summary>Helper to write a alternate multi-block PDU packet.</summary>
virtual void writeRF_TSDU_AMBT(lc::AMBT* ambt, bool clearBeforeWrite = false);
/*
@ -221,6 +224,9 @@ namespace p25
/// <summary>Helper to write a location registration response packet.</summary>
bool writeRF_TSDU_Loc_Reg_Rsp(uint32_t srcId, uint32_t dstId, bool grp);
/// <summary>Helper to write a LLA demand.</summary>
void writeRF_TSDU_Auth_Dmd(uint32_t srcId);
/// <summary>Helper to write a call termination packet.</summary>
bool writeNet_TSDU_Call_Term(uint32_t srcId, uint32_t dstId);

Loading…
Cancel
Save

Powered by TurnKey Linux.