diff --git a/keywords.txt b/keywords.txt index f53f5d1c..b0780771 100644 --- a/keywords.txt +++ b/keywords.txt @@ -307,11 +307,9 @@ getBufferNonces KEYWORD2 setBufferNonces KEYWORD2 getBufferSession KEYWORD2 setBufferSession KEYWORD2 -restore KEYWORD2 beginOTAA KEYWORD2 beginABP KEYWORD2 isJoined KEYWORD2 -saveSession KEYWORD2 sendMacCommandReq KEYWORD2 uplink KEYWORD2 downlink KEYWORD2 diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 866ce109..89c1fe9d 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -47,9 +47,17 @@ void LoRaWANNode::setCSMA(uint8_t backoffMax, uint8_t difsSlots, bool enableCSMA void LoRaWANNode::wipe() { memset(this->bufferNonces, 0, RADIOLIB_LW_NONCES_BUF_SIZE); memset(this->bufferSession, 0, RADIOLIB_LW_SESSION_BUF_SIZE); + memset(&(this->commandsUp), 0, sizeof(LoRaWANMacCommandQueue_t)); + memset(&(this->commandsDown), 0, sizeof(LoRaWANMacCommandQueue_t)); + this->devNonce = 0; + this->joinNonce = 0; } uint8_t* LoRaWANNode::getBufferNonces() { + // generate the signature of the Nonces buffer, and store it in the last two bytes of the Nonces buffer + uint16_t signature = LoRaWANNode::checkSum16(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE - 2); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE], signature); + return(this->bufferNonces); } @@ -65,6 +73,9 @@ int16_t LoRaWANNode::setBufferNonces(uint8_t* persistentBuffer) { // copy the whole buffer over memcpy(this->bufferNonces, persistentBuffer, RADIOLIB_LW_NONCES_BUF_SIZE); + this->devNonce = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_DEV_NONCE]); + this->joinNonce = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_JOIN_NONCE], 3); + // revert to inactive as long as no session is restored this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false; @@ -72,8 +83,20 @@ int16_t LoRaWANNode::setBufferNonces(uint8_t* persistentBuffer) { } uint8_t* LoRaWANNode::getBufferSession() { - // update buffer contents - this->saveSession(); + // store all frame counters + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_A_FCNT_DOWN], this->aFCntDown); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_N_FCNT_DOWN], this->nFCntDown); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_CONF_FCNT_UP], this->confFCntUp); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_CONF_FCNT_DOWN], this->confFCntDown); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_ADR_FCNT], this->adrFCnt); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_FCNT_UP], this->fCntUp); + + // save the current uplink MAC command queue + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_MAC_QUEUE_UL], &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t)); + + // generate the signature of the Session buffer, and store it in the last two bytes of the Session buffer + uint16_t signature = LoRaWANNode::checkSum16(this->bufferSession, RADIOLIB_LW_SESSION_BUF_SIZE - 2); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_SIGNATURE], signature); return(this->bufferSession); } @@ -98,73 +121,12 @@ int16_t LoRaWANNode::setBufferSession(uint8_t* persistentBuffer) { // copy the whole buffer over memcpy(this->bufferSession, persistentBuffer, RADIOLIB_LW_SESSION_BUF_SIZE); - // as both the Nonces and session are restored, revert to active session - this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)true; - - return(state); -} - -int16_t LoRaWANNode::checkBufferCommon(uint8_t *buffer, uint16_t size) { - // check if there are actually values in the buffer - size_t i = 0; - for(; i < size; i++) { - if(buffer[i]) { - break; - } - } - if(i == size) { - return(RADIOLIB_ERR_NETWORK_NOT_JOINED); - } - - // check integrity of the whole buffer (compare checksum to included checksum) - uint16_t checkSum = LoRaWANNode::checkSum16(buffer, size - 2); - uint16_t signature = LoRaWANNode::ntoh(&buffer[size - 2]); - if(signature != checkSum) { - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Calculated checksum: %04X, expected: %04X", checkSum, signature); - return(RADIOLIB_ERR_CHECKSUM_MISMATCH); - } - return(RADIOLIB_ERR_NONE); -} - -int16_t LoRaWANNode::restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass, uint8_t freqPlan) { - // if already joined, ignore - if(this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE]) { - return(RADIOLIB_ERR_NONE); - } - - bool isSameKeys = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_CHECKSUM]) == checkSum; - bool isSameMode = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_MODE]) == lwMode; - bool isSameClass = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_CLASS]) == lwClass; - bool isSamePlan = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_PLAN]) == freqPlan; - - // check if Nonces buffer matches the current configuration - if(!isSameKeys || !isSameMode || !isSameClass || !isSamePlan) { - // if configuration did not match, discard whatever is currently in the buffers and start fresh - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Configuration mismatch (checksum: %d, mode: %d, class: %d, plan: %d)", isSameKeys, isSameMode, isSameClass, isSamePlan); - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Nonces buffer:"); - RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE); - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Clearing buffer and starting fresh"); - this->wipe(); - return(RADIOLIB_ERR_NETWORK_NOT_JOINED); - } - - if(lwMode == RADIOLIB_LW_MODE_OTAA) { - // Nonces buffer is OK, so we can at least restore Nonces - this->devNonce = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_DEV_NONCE]); - this->joinNonce = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_JOIN_NONCE], 3); - } - + //// this code can be used in case breaking chances must be caught: // uint8_t nvm_table_version = this->bufferNonces[RADIOLIB_LW_NONCES_VERSION]; // if (RADIOLIB_LW_NONCES_VERSION_VAL > nvm_table_version) { // // set default values for variables that are new or something // } - if(this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] == 0) { - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("No active session in progress; please join the network"); - RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE); - return(RADIOLIB_ERR_NETWORK_NOT_JOINED); - } - // pull all authentication keys from persistent storage this->devAddr = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LW_SESSION_DEV_ADDR]); memcpy(this->appSKey, &this->bufferSession[RADIOLIB_LW_SESSION_APP_SKEY], RADIOLIB_AES128_BLOCK_SIZE); @@ -183,22 +145,52 @@ int16_t LoRaWANNode::restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass this->adrFCnt = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LW_SESSION_ADR_FCNT]); this->fCntUp = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LW_SESSION_FCNT_UP]); - int16_t state = RADIOLIB_ERR_UNKNOWN; - - // for dynamic bands, first restore the defined channels before restoring ADR - if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { - // restore the defined channels - state = this->restoreChannels(); - RADIOLIB_ASSERT(state); - } - // restore the complete MAC state + + // all-zero buffer used for checking if MAC commands are set + uint8_t bufferZeroes[RADIOLIB_LW_MAX_MAC_COMMAND_LEN_DOWN] = { 0 }; + LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LW_MAC_TX_PARAM_SETUP, + .cid = 0, .payload = { 0 }, - .len = MacTable[RADIOLIB_LW_MAC_TX_PARAM_SETUP].lenDn, + .len = 0, .repeat = 0, }; + + // for dynamic bands, first restore the defined channels before restoring ADR + // this is because the ADR command acts as a mask for the enabled channels + if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { + // setup the default channels + state = this->setupChannelsDyn(); + RADIOLIB_ASSERT(state); + + // restore the session channels + uint8_t *startChannelsUp = &this->bufferSession[RADIOLIB_LW_SESSION_UL_CHANNELS]; + + cmd.cid = RADIOLIB_LW_MAC_NEW_CHANNEL; + for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + cmd.len = MacTable[RADIOLIB_LW_MAC_NEW_CHANNEL].lenDn; + memcpy(cmd.payload, startChannelsUp + (i * cmd.len), cmd.len); + if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { // only execute if it is not all zeroes + cmd.repeat = 1; + (void)execMacCommand(&cmd); + } + } + + uint8_t *startChannelsDown = &this->bufferSession[RADIOLIB_LW_SESSION_DL_CHANNELS]; + + cmd.cid = RADIOLIB_LW_MAC_DL_CHANNEL; + for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + cmd.len = MacTable[RADIOLIB_LW_MAC_DL_CHANNEL].lenDn; + memcpy(cmd.payload, startChannelsDown + (i * cmd.len), cmd.len); + if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { // only execute if it is not all zeroes + (void)execMacCommand(&cmd); + } + } + } + + cmd.cid = RADIOLIB_LW_MAC_TX_PARAM_SETUP, + cmd.len = MacTable[RADIOLIB_LW_MAC_TX_PARAM_SETUP].lenDn, memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LW_SESSION_TX_PARAM_SETUP], cmd.len); (void)execMacCommand(&cmd); @@ -209,8 +201,26 @@ int16_t LoRaWANNode::restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass // for fixed bands, first restore ADR, then the defined channels if(this->band->bandType == RADIOLIB_LW_BAND_FIXED) { - state = this->restoreChannels(); + // setup the default channels + state = this->setupChannelsFix(this->subBand); RADIOLIB_ASSERT(state); + + // restore the session channels + uint8_t *startMACpayload = &this->bufferSession[RADIOLIB_LW_SESSION_UL_CHANNELS]; + + // there are at most 8 channel masks present + cmd.cid = RADIOLIB_LW_MAC_LINK_ADR; + for(int i = 0; i < 8; i++) { + cmd.len = MacTable[RADIOLIB_LW_MAC_LINK_ADR].lenDn; + memcpy(cmd.payload, startMACpayload + (i * cmd.len), cmd.len); + // there COULD, according to spec, be an all zeroes ADR command - meh + if(memcmp(cmd.payload, bufferZeroes, cmd.len) == 0) { + break; + } + cmd.repeat = (i+1); + (void)execMacCommand(&cmd); + } + } cmd.cid = RADIOLIB_LW_MAC_DUTY_CYCLE; @@ -241,73 +251,54 @@ int16_t LoRaWANNode::restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass // copy uplink MAC command queue back in place memcpy(&this->commandsUp, &this->bufferSession[RADIOLIB_LW_SESSION_MAC_QUEUE_UL], sizeof(LoRaWANMacCommandQueue_t)); + // as both the Nonces and session are restored, revert to active session + this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)true; + return(state); } -int16_t LoRaWANNode::restoreChannels() { - // first do the default channels, in case these are not covered by restored channels - if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { - this->setupChannelsDyn(false); - } else { // RADIOLIB_LW_BAND_FIXED - this->setupChannelsFix(this->subBand); +int16_t LoRaWANNode::checkBufferCommon(uint8_t *buffer, uint16_t size) { + // check if there are actually values in the buffer + size_t i = 0; + for(; i < size; i++) { + if(buffer[i]) { + break; + } + } + if(i == size) { + return(RADIOLIB_ERR_NETWORK_NOT_JOINED); } - uint8_t bufferZeroes[5] = { 0 }; - if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { - uint8_t *startChannelsUp = &this->bufferSession[RADIOLIB_LW_SESSION_UL_CHANNELS]; - - LoRaWANMacCommand_t cmd = { .cid = RADIOLIB_LW_MAC_NEW_CHANNEL, .payload = { 0 }, .len = 0, .repeat = 0 }; - for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { - cmd.len = MacTable[RADIOLIB_LW_MAC_NEW_CHANNEL].lenDn; - memcpy(cmd.payload, startChannelsUp + (i * cmd.len), cmd.len); - if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { // only execute if it is not all zeroes - cmd.repeat = 1; - (void)execMacCommand(&cmd); - } - } - - uint8_t *startChannelsDown = &this->bufferSession[RADIOLIB_LW_SESSION_DL_CHANNELS]; - - cmd.cid = RADIOLIB_LW_MAC_DL_CHANNEL; - for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { - cmd.len = MacTable[RADIOLIB_LW_MAC_DL_CHANNEL].lenDn; - memcpy(cmd.payload, startChannelsDown + (i * cmd.len), cmd.len); - if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { // only execute if it is not all zeroes - (void)execMacCommand(&cmd); - } - } - - } else { // RADIOLIB_LW_BAND_FIXED - uint8_t *startMACpayload = &this->bufferSession[RADIOLIB_LW_SESSION_UL_CHANNELS]; - - LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LW_MAC_LINK_ADR, - .payload = { 0 }, - .len = 0, - .repeat = 0, - }; - - // there are at most 8 channel masks present - for(int i = 0; i < 8; i++) { - cmd.len = MacTable[RADIOLIB_LW_MAC_LINK_ADR].lenDn; - memcpy(cmd.payload, startMACpayload + (i * cmd.len), cmd.len); - // there COULD, according to spec, be an all zeroes ADR command - meh - if(memcmp(cmd.payload, bufferZeroes, cmd.len) == 0) { - break; - } - cmd.repeat = (i+1); - (void)execMacCommand(&cmd); - } + // check integrity of the whole buffer (compare checksum to included checksum) + uint16_t checkSum = LoRaWANNode::checkSum16(buffer, size - 2); + uint16_t signature = LoRaWANNode::ntoh(&buffer[size - 2]); + if(signature != checkSum) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Calculated checksum: %04X, expected: %04X", checkSum, signature); + return(RADIOLIB_ERR_CHECKSUM_MISMATCH); } return(RADIOLIB_ERR_NONE); } -void LoRaWANNode::beginCommon(uint8_t initialDr) { - // in case a new session is started while there is an ongoing session - // clear the MAC queues completely - memset(&(this->commandsUp), 0, sizeof(LoRaWANMacCommandQueue_t)); - memset(&(this->commandsDown), 0, sizeof(LoRaWANMacCommandQueue_t)); +bool LoRaWANNode::verifyBuffers(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass, uint8_t freqPlan) { + bool isSameKeys = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_CHECKSUM]) == checkSum; + bool isSameMode = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_MODE]) == lwMode; + bool isSameClass = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_CLASS]) == lwClass; + bool isSamePlan = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_PLAN]) == freqPlan; + // check if Nonces buffer matches the current configuration + if(!isSameKeys || !isSameMode || !isSameClass || !isSamePlan) { + // if configuration did not match, discard whatever is currently in the buffers and start fresh + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Configuration mismatch (checksum: %d, mode: %d, class: %d, plan: %d)", isSameKeys, isSameMode, isSameClass, isSamePlan); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Nonces buffer:"); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE); + return(false); + } + + return(true); +} + + +void LoRaWANNode::beginCommon(uint8_t initialDr) { uint8_t drUp = 0; if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { // if join datarate is user-specified and valid, select that value @@ -447,14 +438,6 @@ void LoRaWANNode::beginCommon(uint8_t initialDr) { } int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, bool force, uint8_t joinDr) { - // if not forced and already joined, don't do anything - if(!force && this->isJoined()) { - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("beginOTAA(): Did not rejoin: session already active"); - return(RADIOLIB_ERR_NONE); - } - - int16_t state = RADIOLIB_ERR_UNKNOWN; - // generate activation key checksum uint16_t checkSum = 0; checkSum ^= LoRaWANNode::checkSum16(reinterpret_cast(&joinEUI), 8); @@ -462,20 +445,21 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe checkSum ^= LoRaWANNode::checkSum16(nwkKey, 16); checkSum ^= LoRaWANNode::checkSum16(appKey, 16); - // if The Force is used, disable the active session; - // as a result, restore() will only restore Nonces if they are available, not the session - if(force) { - this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false; + // if the supplied activation info doesn't match the restored buffers, discard all contents of previous session + if(!this->verifyBuffers(checkSum, RADIOLIB_LW_MODE_OTAA, RADIOLIB_LW_CLASS_A, this->band->bandNum)) { + this->wipe(); } - state = this->restore(checkSum, RADIOLIB_LW_MODE_OTAA, RADIOLIB_LW_CLASS_A, this->band->bandNum); - - if(!force) { - return(state); + // if the device is activated with a valid session, and user didn't force a new session, return + if(this->isJoined() && !force) { + return(RADIOLIB_ERR_NONE); } - Module* mod = this->phyLayer->getMod(); + int16_t state = RADIOLIB_ERR_UNKNOWN; + // either no valid session was found or user forced a new session, so set active-bit to false + this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false; + // setup join-request uplink/downlink frequencies and datarates if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { state = this->setupChannelsDyn(true); @@ -502,10 +486,6 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe // copy devNonce currently in use uint16_t devNonceUsed = this->devNonce; - // increment devNonce as we are sending another join-request - this->devNonce += 1; - - LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_DEV_NONCE], this->devNonce); // build the join-request message uint8_t joinRequestMsg[RADIOLIB_LW_JOIN_REQUEST_LEN]; @@ -521,10 +501,15 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe LoRaWANNode::hton(&joinRequestMsg[RADIOLIB_LW_JOIN_REQUEST_LEN - sizeof(uint32_t)], mic); // send it + Module* mod = this->phyLayer->getMod(); state = this->phyLayer->transmit(joinRequestMsg, RADIOLIB_LW_JOIN_REQUEST_LEN); this->rxDelayStart = mod->hal->millis(); - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Join-request sent <-- Rx Delay start"); RADIOLIB_ASSERT(state); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Join-request sent <-- Rx Delay start"); + + // join-request successfully sent, so increase & save devNonce + this->devNonce += 1; + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_DEV_NONCE], this->devNonce); // configure Rx delay for join-accept message - these are re-configured once a valid join-request is received this->rxDelays[0] = RADIOLIB_LW_JOIN_ACCEPT_DELAY_1_MS; @@ -718,19 +703,26 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe uint16_t signature = LoRaWANNode::checkSum16(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE - 2); LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE], signature); + // store DevAddr and all keys + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_DEV_ADDR], this->devAddr); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_APP_SKEY], this->appSKey, RADIOLIB_AES128_BLOCK_SIZE); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_NWK_SENC_KEY], this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_FNWK_SINT_KEY], this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_SNWK_SINT_KEY], this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); + + // copy the signature of the Nonces buffer over to the Session buffer + uint16_t noncesSignature = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE]); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_NONCES_SIGNATURE], noncesSignature); + + // store network parameters + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_HOMENET_ID], this->homeNetId); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_VERSION], this->rev); + return(RADIOLIB_ERR_NONE); } int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, uint8_t* nwkSEncKey, uint8_t* appSKey, bool force, uint8_t initialDr) { - // if not forced and already joined, don't do anything - if(!force && this->isJoined()) { - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("beginABP(): Did not rejoin: session already active"); - return(RADIOLIB_ERR_NONE); - } - - int16_t state = RADIOLIB_ERR_UNKNOWN; - - // check if we actually need to restart from a clean session + // generate activation key checksum uint16_t checkSum = 0; checkSum ^= LoRaWANNode::checkSum16(reinterpret_cast(&addr), 4); checkSum ^= LoRaWANNode::checkSum16(nwkSEncKey, 16); @@ -738,18 +730,19 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwk if(fNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(fNwkSIntKey, 16); } if(sNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(sNwkSIntKey, 16); } - // if The Force is used, disable the active session; - // as a result, restore() will not restore the session (and there are no Nonces in ABP mode) - if(force) { - this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false; + // if the supplied activation info doesn't match the restored buffers, discard all contents of previous session + if(!this->verifyBuffers(checkSum, RADIOLIB_LW_MODE_OTAA, RADIOLIB_LW_CLASS_A, this->band->bandNum)) { + this->wipe(); } - state = this->restore(checkSum, RADIOLIB_LW_MODE_ABP, RADIOLIB_LW_CLASS_A, this->band->bandNum); - - if(!force) { - return(state); + // if the device is activated with a valid session, and user didn't force a new session, return + if(this->isJoined() && !force) { + return(RADIOLIB_ERR_NONE); } + // either no valid session was found or user forced a new session, so set active-bit to false + this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false; + this->devAddr = addr; memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE); memcpy(this->nwkSEncKey, nwkSEncKey, RADIOLIB_AES128_KEY_SIZE); @@ -788,27 +781,20 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwk LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_PLAN], this->band->bandNum); LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_CHECKSUM], checkSum); + // new session all good, so set active-bit to true this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)true; // generate the signature of the Nonces buffer, and store it in the last two bytes of the Nonces buffer uint16_t signature = LoRaWANNode::checkSum16(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE - 2); LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE], signature); - return(RADIOLIB_ERR_NONE); -} - -bool LoRaWANNode::isJoined() { - return(this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE]); -} - -int16_t LoRaWANNode::saveSession() { // store DevAddr and all keys LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_DEV_ADDR], this->devAddr); memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_APP_SKEY], this->appSKey, RADIOLIB_AES128_BLOCK_SIZE); memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_NWK_SENC_KEY], this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE); memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_FNWK_SINT_KEY], this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_SNWK_SINT_KEY], this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); - + // copy the signature of the Nonces buffer over to the Session buffer uint16_t noncesSignature = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE]); LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_NONCES_SIGNATURE], noncesSignature); @@ -817,24 +803,13 @@ int16_t LoRaWANNode::saveSession() { LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_HOMENET_ID], this->homeNetId); LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_VERSION], this->rev); - // store all frame counters - LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_A_FCNT_DOWN], this->aFCntDown); - LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_N_FCNT_DOWN], this->nFCntDown); - LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_CONF_FCNT_UP], this->confFCntUp); - LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_CONF_FCNT_DOWN], this->confFCntDown); - LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_ADR_FCNT], this->adrFCnt); - LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_FCNT_UP], this->fCntUp); - - // save the current uplink MAC command queue - memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_MAC_QUEUE_UL], &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t)); - - // generate the signature of the Session buffer, and store it in the last two bytes of the Session buffer - uint16_t signature = LoRaWANNode::checkSum16(this->bufferSession, RADIOLIB_LW_SESSION_BUF_SIZE - 2); - LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_SIGNATURE], signature); - return(RADIOLIB_ERR_NONE); } +bool LoRaWANNode::isJoined() { + return(this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE]); +} + #if defined(RADIOLIB_BUILD_ARDUINO) int16_t LoRaWANNode::uplink(String& str, uint8_t fPort, bool isConfirmed, LoRaWANEvent_t* event) { return(this->uplink(str.c_str(), fPort, isConfirmed, event)); diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index f16398e4..66def716 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -544,12 +544,6 @@ class LoRaWANNode { */ int16_t setBufferSession(uint8_t* persistentBuffer); - /*! - \brief Restore session by loading information from persistent storage. - \returns \ref status_codes - */ - int16_t restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass, uint8_t freqPlan); - /*! \brief Join network by performing over-the-air activation. By this procedure, the device will perform an exchange with the network server and set all necessary configuration. @@ -581,12 +575,6 @@ class LoRaWANNode { /*! \brief Whether there is an ongoing session active */ bool isJoined(); - /*! - \brief Save the current state of the session to the session buffer. - \returns \ref status_codes - */ - int16_t saveSession(); - /*! \brief Add a MAC command to the uplink queue. Only LinkCheck and DeviceTime are available to the user. @@ -956,6 +944,9 @@ class LoRaWANNode { // save the selected sub-band in case this must be restored in ADR control uint8_t subBand = 0; + // check if restored buffers match the supplied activation info + bool verifyBuffers(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass, uint8_t freqPlan); + // wait for, open and listen during Rx1 and Rx2 windows; only performs listening int16_t downlinkCommon();