RadioLibSmol/src/protocols/LoRaWAN/LoRaWAN.cpp
2023-07-06 18:50:04 +02:00

503 lines
20 KiB
C++

#include "LoRaWAN.h"
#include <string.h>
#if !defined(RADIOLIB_EXCLUDE_LORAWAN)
// flag to indicate whether we have received a downlink
static volatile bool downlinkReceived = false;
// interrupt service routine to handle downlinks automatically
#if defined(ESP8266) || defined(ESP32)
IRAM_ATTR
#endif
static void LoRaWANNodeOnDownlink(void) {
downlinkReceived = true;
}
LoRaWANNode::LoRaWANNode(PhysicalLayer* phy, const LoRaWANBand_t* band) {
this->phyLayer = phy;
this->band = band;
}
void LoRaWANNode::wipe() {
Module* mod = this->phyLayer->getMod();
mod->hal->wipePersistentStorage();
}
int16_t LoRaWANNode::begin() {
int16_t state = this->setPhyProperties();
RADIOLIB_ASSERT(state);
// check the magic value
Module* mod = this->phyLayer->getMod();
if(mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID) != RADIOLIB_LORAWAN_MAGIC) {
// the magic value is not set, user will have to do perform the join procedure
return(RADIOLIB_ERR_CHIP_NOT_FOUND);
}
// pull all needed information from persistent storage
this->devAddr = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID);
RADIOLIB_DEBUG_PRINTLN("devAddr = 0x%08x", this->devAddr);
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID), this->appSKey, RADIOLIB_AES128_BLOCK_SIZE);
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID), this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID), this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE);
return(RADIOLIB_ERR_NONE);
}
int16_t LoRaWANNode::beginOTAA(uint64_t appEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, bool force) {
// check if we actually need to send the join request
Module* mod = this->phyLayer->getMod();
if(!force && (mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID) == RADIOLIB_LORAWAN_MAGIC)) {
// the device has joined already, we can just pull the data from persistent storage
return(this->begin());
}
// set the physical layer configuration
int16_t state = this->setPhyProperties();
RADIOLIB_ASSERT(state);
// get dev nonce from persistent storage and increment it
uint16_t devNonce = mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID);
mod->hal->setPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID, devNonce + 1);
RADIOLIB_DEBUG_PRINTLN("devNonce = %d", devNonce);
// build the join-request message
uint8_t joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_LEN];
// set the packet fields
joinRequestMsg[0] = RADIOLIB_LORAWAN_MHDR_MTYPE_JOIN_REQUEST | RADIOLIB_LORAWAN_MHDR_MAJOR_R1;
LoRaWANNode::hton<uint64_t>(&joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_JOIN_EUI_POS], appEUI);
LoRaWANNode::hton<uint64_t>(&joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_DEV_EUI_POS], devEUI);
LoRaWANNode::hton<uint16_t>(&joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_DEV_NONCE_POS], devNonce);
// add the authentication code
uint32_t mic = this->generateMIC(joinRequestMsg, RADIOLIB_LORAWAN_JOIN_REQUEST_LEN - sizeof(uint32_t), nwkKey);
LoRaWANNode::hton<uint32_t>(&joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_LEN - sizeof(uint32_t)], mic);
// send it
state = this->phyLayer->transmit(joinRequestMsg, RADIOLIB_LORAWAN_JOIN_REQUEST_LEN);
RADIOLIB_ASSERT(state);
// set the function that will be called when the reply is received
this->phyLayer->setPacketReceivedAction(LoRaWANNodeOnDownlink);
// downlink messages are sent with interted IQ
state = this->phyLayer->invertIQ(true);
RADIOLIB_ASSERT(state);
// start receiving
uint32_t start = mod->hal->millis();
state = this->phyLayer->startReceive();
RADIOLIB_ASSERT(state);
// wait for the reply or timeout
while(!downlinkReceived) {
if(mod->hal->millis() - start >= RADIOLIB_LORAWAN_JOIN_ACCEPT_DELAY_2_MS + 2000) {
downlinkReceived = false;
this->phyLayer->invertIQ(false);
return(RADIOLIB_ERR_RX_TIMEOUT);
}
}
// we have a message, reset the IQ inversion
downlinkReceived = false;
this->phyLayer->clearPacketReceivedAction();
state = this->phyLayer->invertIQ(false);
RADIOLIB_ASSERT(state);
// build the buffer for the reply data
uint8_t joinAcceptMsgEnc[RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN];
// check received length
size_t lenRx = this->phyLayer->getPacketLength(true);
if((lenRx != RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN) && (lenRx != RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN - RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN)) {
RADIOLIB_DEBUG_PRINTLN("joinAccept reply length mismatch, expected %luB got %luB", RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN, lenRx);
return(RADIOLIB_ERR_RX_TIMEOUT);
}
// read the packet
state = this->phyLayer->readData(joinAcceptMsgEnc, lenRx);
RADIOLIB_ASSERT(state);
// check reply message type
if(joinAcceptMsgEnc[0] & RADIOLIB_LORAWAN_MHDR_MTYPE_MASK != RADIOLIB_LORAWAN_MHDR_MTYPE_JOIN_ACCEPT) {
RADIOLIB_DEBUG_PRINTLN("joinAccept reply message type invalid, expected 0x%02x got 0x%02x", RADIOLIB_LORAWAN_MHDR_MTYPE_JOIN_ACCEPT, joinAcceptMsgEnc[0]);
return(RADIOLIB_ERR_RX_TIMEOUT);
}
// decrypt the join accept message
// this is done by encrypting again in ECB mode
// the first byte is the MAC header which is not encrpyted
uint8_t joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN];
joinAcceptMsg[0] = joinAcceptMsgEnc[0];
RadioLibAES128Instance.init(nwkKey);
RadioLibAES128Instance.encryptECB(&joinAcceptMsgEnc[1], RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN - 1, &joinAcceptMsg[1]);
//Module::hexdump(joinAcceptMsg, RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN);
// verify MIC
if(!verifyMIC(joinAcceptMsg, lenRx, nwkKey)) {
return(RADIOLIB_ERR_CRC_MISMATCH);
}
// parse the contents
uint32_t joinNonce = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], 3);
uint32_t homeNetId = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], 3);
this->devAddr = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS]);
uint8_t dlSettings = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DL_SETTINGS_POS];
this->rxDelay = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_RX_DELAY_POS];
// process CFlist if present
if(lenRx == RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN) {
if(this->band->cfListType == RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES) {
// list of frequencies
for(uint8_t i = 0; i < 5; i++) {
uint32_t freq = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_POS + 3*i], 3);
availableChannelsFreq[i] = (float)freq/10000.0;
RADIOLIB_DEBUG_PRINTLN("Channel %d frequency = %f MHz", i, availableChannelsFreq[i]);
}
} else {
// TODO list of masks
RADIOLIB_DEBUG_PRINTLN("CFlist masks not supported (yet)");
return(RADIOLIB_ERR_UNSUPPORTED_ENCODING);
}
}
RADIOLIB_DEBUG_PRINTLN("joinNonce = %lu", joinNonce);
RADIOLIB_DEBUG_PRINTLN("homeNetId = %lu", homeNetId);
RADIOLIB_DEBUG_PRINTLN("devAddr = 0x%08x", devAddr);
RADIOLIB_DEBUG_PRINTLN("dlSettings = 0x%02x", dlSettings);
RADIOLIB_DEBUG_PRINTLN("rxDelay = %d", this->rxDelay);
// prepare buffer for key derivation
uint8_t keyDerivationBuff[RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
LoRaWANNode::hton<uint32_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], joinNonce, 3);
LoRaWANNode::hton<uint32_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], homeNetId, 3);
LoRaWANNode::hton<uint16_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS], devNonce);
// check protocol version (1.0 vs 1.1)
if(dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) {
// TODO implement 1.1
this->rev = 1;
RADIOLIB_DEBUG_PRINTLN("LoRaWAN 1.1 not supported (yet)");
(void)appKey;
return(RADIOLIB_ERR_UNSUPPORTED_ENCODING);
} else {
// 1.0 version, just derive the keys
this->rev = 0;
keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_APP_S_KEY;
RadioLibAES128Instance.init(nwkKey);
RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->appSKey);
keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_F_NWK_S_INT_KEY;
RadioLibAES128Instance.init(nwkKey);
RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->fNwkSIntKey);
memcpy(this->sNwkSIntKey, this->fNwkSIntKey, RADIOLIB_AES128_KEY_SIZE);
memcpy(this->nwkSEncKey, this->fNwkSIntKey, RADIOLIB_AES128_KEY_SIZE);
}
// save the device address
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID, this->devAddr);
// update the keys
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID), this->appSKey, RADIOLIB_AES128_BLOCK_SIZE);
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID), this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID), this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE);
// all complete, reset device counters and set the magic number
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, 0);
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID, RADIOLIB_LORAWAN_MAGIC);
return(RADIOLIB_ERR_NONE);
}
int16_t LoRaWANNode::beginAPB(uint32_t addr, uint8_t net, uint8_t* nwkSKey, uint8_t* appSKey) {
this->devAddr = (((uint32_t)net << 25) & 0xFE000000) | (addr & 0x01FFFFFF);
memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE);
memcpy(this->nwkSEncKey, nwkSKey, RADIOLIB_AES128_KEY_SIZE);
return(RADIOLIB_ERR_NONE);
}
#if defined(RADIOLIB_BUILD_ARDUINO)
int16_t LoRaWANNode::uplink(String& str, uint8_t port) {
return(this->uplink(str.c_str(), port));
}
#endif
int16_t LoRaWANNode::uplink(const char* str, uint8_t port) {
return(this->uplink((uint8_t*)str, strlen(str), port));
}
int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
// check destination port
RADIOLIB_CHECK_RANGE(port, 0x01, 0xDF, RADIOLIB_ERR_INVALID_PAYLOAD);
// check maximum payload len as defiend in phy
// TODO implement Fopts
uint8_t foptsLen = 0;
if(len > this->band->payloadLenMax[this->dataRate]) {
return(RADIOLIB_ERR_PACKET_TOO_LONG);
}
// build the uplink message
// the first 16 bytes are reserved for MIC calculation blocks
size_t uplinkMsgLen = RADIOLIB_LORAWAN_UPLINK_LEN(len, foptsLen);
#if defined(RADIOLIB_STATIC_ONLY)
uint8_t uplinkMsg[RADIOLIB_STATIC_ARRAY_SIZE];
#else
uint8_t* uplinkMsg = new uint8_t[uplinkMsgLen];
#endif
// set the packet fields
uplinkMsg[RADIOLIB_LORAWAN_UPLINK_LEN_START_OFFS] = RADIOLIB_LORAWAN_MHDR_MTYPE_UNCONF_DATA_UP | RADIOLIB_LORAWAN_MHDR_MAJOR_R1;
LoRaWANNode::hton<uint32_t>(&uplinkMsg[RADIOLIB_LORAWAN_UPLINK_DEV_ADDR_POS], this->devAddr);
// TODO implement adaptive data rate
uplinkMsg[RADIOLIB_LORAWAN_UPLINK_FCTRL_POS] = 0x00;
// get frame counter from persistent storage
Module* mod = this->phyLayer->getMod();
uint32_t fcnt = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID);
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, fcnt + 1);
LoRaWANNode::hton<uint16_t>(&uplinkMsg[RADIOLIB_LORAWAN_UPLINK_FCNT_POS], (uint16_t)fcnt);
// TODO implement FOpts
uplinkMsg[RADIOLIB_LORAWAN_UPLINK_FPORT_POS(foptsLen)] = port;
// select encryption key based on the target port
uint8_t* encKey = this->appSKey;
if(port == 0) {
encKey = this->nwkSEncKey;
}
// figure out how many encryption blocks are there
size_t numBlocks = len/RADIOLIB_AES128_BLOCK_SIZE;
if(len % RADIOLIB_AES128_BLOCK_SIZE) {
numBlocks++;
}
// generate the encryption blocks
uint8_t encBuffer[RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
uint8_t encBlock[RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
encBlock[RADIOLIB_LORAWAN_BLOCK_MAGIC_POS] = RADIOLIB_LORAWAN_ENC_BLOCK_MAGIC;
encBlock[RADIOLIB_LORAWAN_BLOCK_DIR_POS] = RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK;
LoRaWANNode::hton<uint32_t>(&encBlock[RADIOLIB_LORAWAN_BLOCK_DEV_ADDR_POS], this->devAddr);
LoRaWANNode::hton<uint32_t>(&encBlock[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt);
//Module::hexdump(uplinkMsg, uplinkMsgLen);
// now encrypt the payload
size_t remLen = len;
for(size_t i = 0; i < numBlocks; i++) {
encBlock[RADIOLIB_LORAWAN_ENC_BLOCK_COUNTER_POS] = i + 1;
// encrypt the buffer
RadioLibAES128Instance.init(encKey);
RadioLibAES128Instance.encryptECB(encBlock, RADIOLIB_AES128_BLOCK_SIZE, encBuffer);
// now xor the buffer with the payload
size_t xorLen = remLen;
if(xorLen > RADIOLIB_AES128_BLOCK_SIZE) {
xorLen = RADIOLIB_AES128_BLOCK_SIZE;
}
for(uint8_t j = 0; j < xorLen; j++) {
uplinkMsg[RADIOLIB_LORAWAN_UPLINK_PAYLOAD_POS(foptsLen) + i*RADIOLIB_AES128_BLOCK_SIZE + j] = data[i*RADIOLIB_AES128_BLOCK_SIZE + j] ^ encBuffer[j];
}
remLen -= xorLen;
}
// create blocks for MIC calculation
uint8_t block0[RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
block0[RADIOLIB_LORAWAN_BLOCK_MAGIC_POS] = RADIOLIB_LORAWAN_MIC_BLOCK_MAGIC;
block0[RADIOLIB_LORAWAN_BLOCK_DIR_POS] = RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK;
LoRaWANNode::hton<uint32_t>(&block0[RADIOLIB_LORAWAN_BLOCK_DEV_ADDR_POS], this->devAddr);
LoRaWANNode::hton<uint32_t>(&block0[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt);
block0[RADIOLIB_LORAWAN_MIC_BLOCK_LEN_POS] = uplinkMsgLen - RADIOLIB_AES128_BLOCK_SIZE - sizeof(uint32_t);
uint8_t block1[RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
memcpy(block1, block0, RADIOLIB_AES128_BLOCK_SIZE);
// TODO implement confirmed frames
block1[RADIOLIB_LORAWAN_MIC_DATA_RATE_POS] = this->dataRate;
block1[RADIOLIB_LORAWAN_MIC_CH_INDEX_POS] = this->chIndex;
//Module::hexdump(uplinkMsg, uplinkMsgLen);
// calculate authentication codes
memcpy(uplinkMsg, block1, RADIOLIB_AES128_BLOCK_SIZE);
uint32_t micS = this->generateMIC(uplinkMsg, uplinkMsgLen - sizeof(uint32_t), this->sNwkSIntKey);
memcpy(uplinkMsg, block0, RADIOLIB_AES128_BLOCK_SIZE);
uint32_t micF = this->generateMIC(uplinkMsg, uplinkMsgLen - sizeof(uint32_t), this->fNwkSIntKey);
// check LoRaWAN revision
if(this->rev == 1) {
uint32_t mic = ((uint32_t)(micS & 0x00FF) << 24) | ((uint32_t)(micS & 0xFF00) << 8) | ((uint32_t)(micF & 0xFF00) >> 8) | ((uint32_t)(micF & 0x00FF) << 8);
LoRaWANNode::hton<uint32_t>(&uplinkMsg[uplinkMsgLen - sizeof(uint32_t)], mic);
} else {
LoRaWANNode::hton<uint32_t>(&uplinkMsg[uplinkMsgLen - sizeof(uint32_t)], micF);
}
//Module::hexdump(uplinkMsg, uplinkMsgLen);
// send it (without the MIC calculation blocks)
int16_t state = this->phyLayer->transmit(&uplinkMsg[RADIOLIB_LORAWAN_UPLINK_LEN_START_OFFS], uplinkMsgLen - RADIOLIB_LORAWAN_UPLINK_LEN_START_OFFS);
#if !defined(RADIOLIB_STATIC_ONLY)
delete[] uplinkMsg;
#endif
RADIOLIB_ASSERT(state);
// TODO implement listening for downlinks in RX1/RX2 slots
return(RADIOLIB_ERR_NONE);
}
int16_t LoRaWANNode::configureChannel(uint8_t chan, uint8_t dr) {
// find the span based on the channel ID
uint8_t span = 0;
uint8_t spanChannelId = 0;
bool found = false;
for(uint8_t chanCtr = 0; span < this->band->numChannelSpans; span++) {
for(; spanChannelId < this->band->defaultChannels[span].numChannels; spanChannelId++) {
if(chanCtr >= chan) {
found = true;
break;
}
chanCtr++;
}
if(found) {
break;
}
}
if(!found) {
return(RADIOLIB_ERR_INVALID_MODULATION_PARAMETERS);
}
this->chIndex = chan;
RADIOLIB_DEBUG_PRINTLN("Channel span %d, channel %d", span, spanChannelId);
// set the frequency
float freq = this->band->defaultChannels[span].freqStart + this->band->defaultChannels[span].freqStep * (float)spanChannelId;
RADIOLIB_DEBUG_PRINTLN("Frequency %f MHz", freq);
int state = this->phyLayer->setFrequency(freq);
RADIOLIB_ASSERT(state);
// set the data rate
uint8_t dataRateBand = this->band->defaultChannels[span].dataRates[dr];
this->dataRate = dr;
if(dr == RADIOLIB_LORAWAN_DATA_RATE_UNUSED) {
// find the first usable data rate
for(uint8_t i = 0; i < RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES; i++) {
if(this->band->defaultChannels[span].dataRates[i] != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) {
dataRateBand = this->band->defaultChannels[span].dataRates[i];
this->dataRate = i;
break;
}
}
}
RADIOLIB_DEBUG_PRINTLN("Data rate %02x", dataRateBand);
DataRate_t datr;
uint8_t bw = dataRateBand & 0x03;
switch(bw) {
case(RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ):
datr.lora.bandwidth = 125.0;
break;
case(RADIOLIB_LORAWAN_DATA_RATE_BW_250_KHZ):
datr.lora.bandwidth = 250.0;
break;
case(RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ):
datr.lora.bandwidth = 500.0;
break;
default:
return(RADIOLIB_ERR_INVALID_BANDWIDTH);
}
datr.lora.spreadingFactor = ((dataRateBand & 0xF0) >> 4) + 6;
state = this->phyLayer->setDataRate(datr);
return(state);
}
uint32_t LoRaWANNode::generateMIC(uint8_t* msg, size_t len, uint8_t* key) {
if((msg == NULL) || (len == 0)) {
return(0);
}
RadioLibAES128Instance.init(key);
uint8_t cmac[RADIOLIB_AES128_BLOCK_SIZE];
RadioLibAES128Instance.generateCMAC(msg, len, cmac);
return(((uint32_t)cmac[0]) | ((uint32_t)cmac[1] << 8) | ((uint32_t)cmac[2] << 16) | ((uint32_t)cmac[3]) << 24);
}
bool LoRaWANNode::verifyMIC(uint8_t* msg, size_t len, uint8_t* key) {
if((msg == NULL) || (len < sizeof(uint32_t))) {
return(0);
}
// extract MIC from the message
uint32_t micReceived = LoRaWANNode::ntoh<uint32_t>(&msg[len - sizeof(uint32_t)]);
// calculate the expected value and compare
uint32_t micCalculated = generateMIC(msg, len - sizeof(uint32_t), key);
if(micCalculated != micReceived) {
RADIOLIB_DEBUG_PRINTLN("MIC mismatch, expected 0x%08x, got 0x%08x", micCalculated, micReceived);
return(false);
}
return(true);
}
int16_t LoRaWANNode::setPhyProperties() {
// set the physical layer configuration
// TODO select channel span based on channel ID
// TODO select channel randomly
uint8_t channelId = 0;
int16_t state = this->configureChannel(channelId, this->band->defaultChannels[0].joinRequestDataRate);
RADIOLIB_ASSERT(state);
state = this->phyLayer->setOutputPower(this->band->powerMax);
RADIOLIB_ASSERT(state);
uint8_t syncWord = RADIOLIB_LORAWAN_LORA_SYNC_WORD;
state = this->phyLayer->setSyncWord(&syncWord, 1);
RADIOLIB_ASSERT(state);
state = this->phyLayer->setPreambleLength(RADIOLIB_LORAWAN_LORA_PREAMBLE_LEN);
return(state);
}
template<typename T>
T LoRaWANNode::ntoh(uint8_t* buff, size_t size) {
uint8_t* buffPtr = buff;
size_t targetSize = sizeof(T);
if(size != 0) {
targetSize = size;
}
T res = 0;
for(uint8_t i = 0; i < targetSize; i++) {
res |= (uint32_t)(*(buffPtr++)) << 8*i;
}
return(res);
}
template<typename T>
void LoRaWANNode::hton(uint8_t* buff, T val, size_t size) {
uint8_t* buffPtr = buff;
size_t targetSize = sizeof(T);
if(size != 0) {
targetSize = size;
}
for(uint8_t i = 0; i < targetSize; i++) {
*(buffPtr++) = val >> 8*i;
}
}
#endif