lw_packets.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. #line __LINE__ "lw_packets.c"
  2. /**************************************************************************
  3. Copyright (c) 2017 Theodor Tobias Rohde (tr@lobaro.com)
  4. Lobaro - Industrial IoT Solutions
  5. www.lobaro.com
  6. Permission is hereby granted, free of charge, to any person obtaining a copy
  7. of this software and associated documentation files (the "Software"), to deal
  8. in the Software without restriction, including without limitation the rights
  9. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. copies of the Software, and to permit persons to whom the Software is
  11. furnished to do so, subject to the following conditions:
  12. The above copyright notice and this permission notice shall be included in all
  13. copies or substantial portions of the Software.
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. SOFTWARE.
  21. *****************************************************************************/
  22. #include <stdint.h>
  23. #include <stdbool.h>
  24. #include <string.h> // for memcpy
  25. #include <stdlib.h> // for NULL
  26. #ifndef lobaroASSERT
  27. #include <assert.h>
  28. #include <inttypes.h>
  29. #define lobaroASSERT(x) assert(x)
  30. #endif
  31. #include "crypto/lw_crypto.h"
  32. #include "lw_packets.h"
  33. typedef struct {
  34. lwPackets_api_t api;
  35. lwPackets_state_t state;
  36. bool initDone;
  37. } lwPacketsLib_t;
  38. static lwPacketsLib_t lib = {.initDone = false}; // holds external dependencies
  39. static uint16_t parseUInt16LittleEndian(uint8_t* bytes) {
  40. return (((uint16_t) bytes[0]) << 0) | (((uint16_t) bytes[1]) << 8);
  41. }
  42. static uint32_t parseUInt32LittleEndian(uint8_t* bytes) {
  43. return (((uint32_t) bytes[0]) << 0) | (((uint32_t) bytes[1]) << 8) | (((uint32_t) bytes[2]) << 16) | (((uint32_t) bytes[3]) << 24);
  44. }
  45. // EUI are 8 bytes multi-octet fields and are transmitted as little endian.
  46. static void convertInPlaceEUI64bufLittleEndian(uint8_t* eui8buf) {
  47. uint8_t tmp[8];
  48. if (eui8buf) {
  49. memcpy(tmp, eui8buf, 8);
  50. for (int i = 0; i < 8; i++) {
  51. eui8buf[i] = tmp[7 - i];
  52. }
  53. }
  54. }
  55. static void logNothingDummy(const char* format, ...) {
  56. return;
  57. // example log function may be implemented by app/user (#include <stdarg.h>, #include <stdio.h>) :
  58. // char buffer[100];
  59. // va_list args;
  60. // va_start(args, format);
  61. // int len = vsnprintf(buffer, sizeof(buffer), format, args);
  62. // if (len > 100 - 1) {
  63. // strcpy(&buffer[100 - 5], "...\n");
  64. // }
  65. // va_end(args);
  66. // puts(buffer); // or custom uart puts
  67. }
  68. void LoRaWAN_PacketsUtil_Init(lwPackets_api_t api, lwPackets_state_t state) {
  69. lib.api = api;
  70. lib.state = state;
  71. if (lib.api.LogError == NULL) {
  72. lib.api.LogError = logNothingDummy;
  73. }
  74. if (lib.api.LogInfo == NULL) {
  75. lib.api.LogInfo = logNothingDummy;
  76. }
  77. if (lib.api.free == NULL || lib.api.malloc == NULL) {
  78. lib.api.LogInfo("LW Packets: using c malloc and free default\n");
  79. lib.api.free = free; // from <stdlib.h>
  80. lib.api.malloc = malloc; // from <stdlib.h>
  81. }
  82. lib.initDone = true;
  83. }
  84. lorawan_packet_t* LoRaWAN_NewPacket(uint8_t* payload, uint8_t length) {
  85. lobaroASSERT(lib.initDone);
  86. lobaroASSERT(length <= 222); // max payload size of lorawan EU868 (see 2.1.6 lorawan 1.1 regional parameters)
  87. lorawan_packet_t* packet = (lorawan_packet_t*) lib.api.malloc(sizeof(lorawan_packet_t));
  88. if (packet == NULL) {
  89. return NULL;
  90. }
  91. memset(packet, 0, sizeof(lorawan_packet_t));
  92. if (payload != NULL && length) {
  93. uint8_t* dataCpy = (uint8_t*) lib.api.malloc(length);
  94. if (dataCpy == NULL) {
  95. lib.api.free(packet);
  96. return NULL;
  97. }
  98. memcpy(dataCpy, payload, length);
  99. packet->BODY.MACPayload.payloadLength = length;
  100. packet->pPayload = dataCpy; // just to get sure if user creates a packet with payload but uses it as join packet afterwards (see also DeletePacket fkt)
  101. }
  102. // static field
  103. packet->MHDR.version = LORAWAN_R1;
  104. return packet;
  105. }
  106. void LoRaWAN_DeletePacket(lorawan_packet_t* packet) {
  107. lobaroASSERT(lib.initDone);
  108. if (packet == NULL) {
  109. return;
  110. }
  111. // don't rely on packets MHDR type if there is any payload memory to free
  112. if (packet->pPayload) {
  113. lib.api.free(packet->pPayload);
  114. packet->pPayload = NULL;
  115. }
  116. lib.api.free(packet);
  117. }
  118. // Marshal the packet into a given buffer, returns the actual size
  119. // Returns -1 when the buffer is too small
  120. uint8_t LoRaWAN_MarshalPacket(lorawan_packet_t* packet, uint8_t* outBuffer, uint8_t bufferSize) {
  121. uint8_t pos = 0;
  122. int optLen = 0;
  123. lw_mic_t mic; // 4 byte lorawan message integrity code (last bytes of PHYPayload)
  124. lw_key_t lw_key; // lorawan aes de/encrypt input struct (see crypto.c for details)
  125. lobaroASSERT(lib.initDone);
  126. if (bufferSize < 4) {
  127. return pos;
  128. }
  129. // MHDR
  130. outBuffer[pos++] = (packet->MHDR.type << 5) | (packet->MHDR.version);
  131. if (packet->MHDR.type == MTYPE_UNCONFIRMED_DATA_UP
  132. || packet->MHDR.type == MTYPE_CONFIRMED_DATA_UP) {
  133. lw_key.link = LW_UPLINK;
  134. // marshaling continues below if
  135. } else if (packet->MHDR.type == MTYPE_UNCONFIRMED_DATA_DOWN
  136. || packet->MHDR.type == MTYPE_CONFIRMED_DATA_DOWN) {
  137. lw_key.link = LW_DOWNLINK;
  138. // marshaling continues below if
  139. } else if (packet->MHDR.type == MTYPE_JOIN_REQUEST) {
  140. // EUI are 8 bytes multi-octet fields and are transmitted as little endian. (LoRaWAN Specification)
  141. // -> if the EUI-64 is 70-B3-D5-7E-F0-00-48-9C it would be in the air as 9C-48-00...
  142. // convertInPlaceEUI64bufLittleEndian performs this byte order inversion in place
  143. if (bufferSize < pos + 8) {
  144. return 0;
  145. }
  146. memcpy(outBuffer + pos, lib.state.pDevCfg->joinEUI, 8);
  147. convertInPlaceEUI64bufLittleEndian(outBuffer + pos);
  148. pos += 8;
  149. if (bufferSize < pos + 8) {
  150. return 0;
  151. }
  152. memcpy(outBuffer + pos, lib.state.pDevCfg->devEUI, 8);
  153. convertInPlaceEUI64bufLittleEndian(outBuffer + pos);
  154. pos += 8;
  155. if (bufferSize < pos + 2) {
  156. return 0;
  157. }
  158. outBuffer[pos++] = (uint8_t) (lib.state.pDevCfg->devnonce & 0xff);
  159. outBuffer[pos++] = (uint8_t) (lib.state.pDevCfg->devnonce >> 8);
  160. lw_key.aeskey = lib.state.pDevCfg->appkey;
  161. lw_key.in = outBuffer;
  162. lw_key.len = pos;
  163. lw_join_mic(&mic, &lw_key);
  164. if (bufferSize < pos + 4) {
  165. return 0;
  166. }
  167. memcpy(outBuffer + pos, mic.buf, 4);
  168. packet->MIC = mic.data;
  169. pos += 4;
  170. return pos;
  171. } else if (packet->MHDR.type == MTYPE_JOIN_ACCEPT) {
  172. // normally not needed by a lorawan device but included for completeness (issued by a network server only!)
  173. outBuffer[pos++] = (uint8_t) (packet->BODY.JoinAccept.JoinNonce >> 0);
  174. outBuffer[pos++] = (uint8_t) (packet->BODY.JoinAccept.JoinNonce >> 8);
  175. outBuffer[pos++] = (uint8_t) (packet->BODY.JoinAccept.JoinNonce >> 16);
  176. outBuffer[pos++] = (uint8_t) (packet->BODY.JoinAccept.HomeNetID >> 0);
  177. outBuffer[pos++] = (uint8_t) (packet->BODY.JoinAccept.HomeNetID >> 8);
  178. outBuffer[pos++] = (uint8_t) (packet->BODY.JoinAccept.HomeNetID >> 16);
  179. outBuffer[pos++] = (uint8_t) (packet->BODY.JoinAccept.DevAddr >> 0);
  180. outBuffer[pos++] = (uint8_t) (packet->BODY.JoinAccept.DevAddr >> 8);
  181. outBuffer[pos++] = (uint8_t) (packet->BODY.JoinAccept.DevAddr >> 16);
  182. outBuffer[pos++] = (uint8_t) (packet->BODY.JoinAccept.DevAddr >> 24);
  183. uint8_t dlsettings = 0;
  184. dlsettings |= ((uint8_t) packet->BODY.JoinAccept.DLsettings.Rx1DRoffset) << 4;
  185. dlsettings |= ((uint8_t) packet->BODY.JoinAccept.DLsettings.Rx2DR);
  186. outBuffer[pos++] = dlsettings;
  187. outBuffer[pos++] = packet->BODY.JoinAccept.RxDelay;
  188. if (packet->BODY.JoinAccept.hasCFlist) {
  189. memcpy(&(outBuffer[pos]), packet->BODY.JoinAccept.CFlist.FreqCH4, 3);
  190. pos += 3;
  191. memcpy(&(outBuffer[pos]), packet->BODY.JoinAccept.CFlist.FreqCH5, 3);
  192. pos += 3;
  193. memcpy(&(outBuffer[pos]), packet->BODY.JoinAccept.CFlist.FreqCH6, 3);
  194. pos += 3;
  195. memcpy(&(outBuffer[pos]), packet->BODY.JoinAccept.CFlist.FreqCH7, 3);
  196. pos += 3;
  197. memcpy(&(outBuffer[pos]), packet->BODY.JoinAccept.CFlist.FreqCH8, 3);
  198. pos += 3;
  199. }
  200. // calc mic
  201. lw_key.aeskey = lib.state.pDevCfg->appkey;
  202. lw_key.in = outBuffer;
  203. lw_key.len = pos;
  204. lw_join_mic(&mic, &lw_key);
  205. memcpy(outBuffer + pos, mic.buf, 4);
  206. pos += 4;
  207. // encryp msg
  208. uint8_t out[33];
  209. lw_key.aeskey = lib.state.pDevCfg->appkey;
  210. lw_key.in = outBuffer + 1; // skip MHDR byte
  211. lw_key.len = pos - 1;
  212. lw_join_encrypt(out, &lw_key);
  213. memcpy(outBuffer + 1, out, lw_key.len);
  214. return pos;
  215. } else {
  216. return 0;
  217. lib.api.LogError("unknown LoRaWAN msg type for marshaling!");
  218. }
  219. // FHDR
  220. if (bufferSize < pos + 4) {
  221. return 0;
  222. }
  223. outBuffer[pos++] = (uint8_t) (packet->BODY.MACPayload.FHDR.DevAddr & 0xff);
  224. outBuffer[pos++] = (uint8_t) (packet->BODY.MACPayload.FHDR.DevAddr >> 8);
  225. outBuffer[pos++] = (uint8_t) (packet->BODY.MACPayload.FHDR.DevAddr >> 16);
  226. outBuffer[pos++] = (uint8_t) (packet->BODY.MACPayload.FHDR.DevAddr >> 24);
  227. lw_key.devaddr.data = packet->BODY.MACPayload.FHDR.DevAddr;
  228. if (lw_key.link == LW_UPLINK) {
  229. // uplink packet
  230. if (bufferSize < pos + 1) {
  231. return 0;
  232. }
  233. outBuffer[pos++] = (packet->BODY.MACPayload.FHDR.FCtrl.uplink.ADR << 7)
  234. | (packet->BODY.MACPayload.FHDR.FCtrl.uplink.ADRACKReq << 6)
  235. | (packet->BODY.MACPayload.FHDR.FCtrl.uplink.ACK << 5)
  236. | (packet->BODY.MACPayload.FHDR.FCtrl.uplink.FOptsLen);
  237. optLen = packet->BODY.MACPayload.FHDR.FCtrl.uplink.FOptsLen;
  238. } else if (packet->MHDR.type == MTYPE_UNCONFIRMED_DATA_DOWN
  239. || packet->MHDR.type == MTYPE_CONFIRMED_DATA_DOWN) {
  240. // downlink packet
  241. if (bufferSize < pos + 1) {
  242. return 0;
  243. }
  244. outBuffer[pos++] = (packet->BODY.MACPayload.FHDR.FCtrl.downlink.ADR << 7)
  245. | (packet->BODY.MACPayload.FHDR.FCtrl.downlink.RFU << 6)
  246. | (packet->BODY.MACPayload.FHDR.FCtrl.downlink.ACK << 5)
  247. | (packet->BODY.MACPayload.FHDR.FCtrl.downlink.FPending << 4)
  248. | (packet->BODY.MACPayload.FHDR.FCtrl.downlink.FOptsLen);
  249. optLen = packet->BODY.MACPayload.FHDR.FCtrl.downlink.FOptsLen;
  250. } else {
  251. lobaroASSERT("join accept, request not implemented yet! (we aren't a network server yet!)");
  252. }
  253. // Little endian
  254. if (bufferSize < pos + 2) {
  255. return 0;
  256. }
  257. outBuffer[pos++] = (uint8_t) (packet->BODY.MACPayload.FHDR.FCnt16 & 0xff);
  258. outBuffer[pos++] = (uint8_t) (packet->BODY.MACPayload.FHDR.FCnt16 >> 8);
  259. lw_key.fcnt32 = packet->BODY.MACPayload.FHDR.FCnt16;
  260. if (optLen) {
  261. if (bufferSize < pos + optLen) {
  262. return 0;
  263. }
  264. memcpy(outBuffer + pos, packet->BODY.MACPayload.FHDR.FOpts, optLen);
  265. pos += optLen;
  266. }
  267. // encrypt payload (if present)
  268. if (packet->BODY.MACPayload.payloadLength != 0) {
  269. if (bufferSize < pos + 1) {
  270. return 0;
  271. }
  272. outBuffer[pos++] = packet->BODY.MACPayload.FPort;
  273. if (packet->BODY.MACPayload.FPort == 0) {
  274. lw_key.aeskey = lib.state.pDevCfg->nwkskey; // todo maybe add this as parameter?
  275. } else {
  276. lw_key.aeskey = lib.state.pDevCfg->appskey;
  277. }
  278. lw_key.in = packet->pPayload;
  279. lw_key.len = packet->BODY.MACPayload.payloadLength;
  280. int cryptedPayloadLength = lw_encrypt(outBuffer + pos, &lw_key);
  281. pos += cryptedPayloadLength;
  282. if (bufferSize < pos) { // TODO: This is too late, we should predict or limit the buffer inside lw_encrypt
  283. lobaroASSERT(false);
  284. return 0;
  285. }
  286. }
  287. // 4 byte MIC
  288. if (bufferSize < pos + 4) {
  289. return 0;
  290. }
  291. lw_key.aeskey = lib.state.pDevCfg->nwkskey;
  292. lw_key.in = outBuffer;
  293. lw_key.len = pos;
  294. lw_msg_mic(&mic, &lw_key);
  295. memcpy(outBuffer + pos, mic.buf, 4);
  296. pos += 4;
  297. return pos;
  298. }
  299. // Like LoRaWAN_NewPacket but takes a marshaled packet as input
  300. // MUST be freed with LoRaWAN_DeletePacket
  301. // data is the raw packet as produced by LoRaWAN_MarshalPacket
  302. lorawan_packet_t* LoRaWAN_UnmarshalPacket(uint8_t* dataToParse, uint8_t length) { // todo add keys as parameter
  303. uint8_t idx;
  304. lw_mic_t micCalc; // calculated mic
  305. lw_key_t lw_key;
  306. lobaroASSERT(lib.initDone);
  307. if (length < 3) {
  308. return NULL;
  309. }
  310. lorawan_packet_t* packet = (lorawan_packet_t*) lib.api.malloc(sizeof(lorawan_packet_t));
  311. if (packet == NULL) {
  312. lib.api.LogError("Lobawan: Out of memory\n");
  313. return NULL;
  314. }
  315. memset(packet, 0, sizeof(lorawan_packet_t));
  316. // MHDR
  317. idx = 0;
  318. packet->MHDR.type = dataToParse[idx] >> 5;
  319. packet->MHDR.version = dataToParse[idx] & 0x3;
  320. idx++;
  321. if (packet->MHDR.type == MTYPE_PROPRIETARY) {
  322. lib.api.LogError("Lobawan: Got proprietary MHDR -> discard msg\n");
  323. lib.api.free(packet);
  324. return NULL;
  325. }
  326. switch (packet->MHDR.type) {
  327. case MTYPE_UNCONFIRMED_DATA_UP:
  328. case MTYPE_CONFIRMED_DATA_UP:
  329. case MTYPE_UNCONFIRMED_DATA_DOWN:
  330. case MTYPE_CONFIRMED_DATA_DOWN:
  331. idx = 1; // skip already parsed MHDR
  332. // get devAdr since we need it for MIC check
  333. packet->BODY.MACPayload.FHDR.DevAddr = parseUInt32LittleEndian(&(dataToParse[idx]));
  334. idx += 4;
  335. uint8_t fctrl = dataToParse[idx];
  336. idx++;
  337. packet->BODY.MACPayload.FHDR.FCnt16 = parseUInt16LittleEndian(&(dataToParse[idx]));
  338. idx += 2;
  339. lw_key.aeskey = lib.state.pDevCfg->nwkskey;
  340. lw_key.in = dataToParse;
  341. lw_key.len = length - 4;
  342. lw_key.devaddr.data = packet->BODY.MACPayload.FHDR.DevAddr;
  343. uint32_t currFcnt32;
  344. bool uplink = false;
  345. if (packet->MHDR.type == MTYPE_UNCONFIRMED_DATA_UP || packet->MHDR.type == MTYPE_UNCONFIRMED_DATA_UP) {
  346. uplink = true;
  347. lw_key.link = LW_UPLINK;
  348. currFcnt32 = lib.state.pFCntCtrl->FCntUp;
  349. packet->BODY.MACPayload.FHDR.FCtrl.uplink.ADR = fctrl >> 7;
  350. packet->BODY.MACPayload.FHDR.FCtrl.uplink.ADRACKReq = (fctrl & (1 << 6)) >> 6;
  351. packet->BODY.MACPayload.FHDR.FCtrl.uplink.ACK = (fctrl & (1 << 5)) >> 5;
  352. packet->BODY.MACPayload.FHDR.FCtrl.uplink.ClassB = (fctrl & (1 << 4)) >> 4;
  353. packet->BODY.MACPayload.FHDR.FCtrl.uplink.FOptsLen = (fctrl & 0x0f);
  354. } else { // downlink
  355. lw_key.link = LW_DOWNLINK;
  356. currFcnt32 = lib.state.pFCntCtrl->AFCntDown;
  357. #if USE_LORAWAN_1_1 == 1
  358. #error "missing implementation for NFCntDown"
  359. #endif
  360. packet->BODY.MACPayload.FHDR.FCtrl.uplink.ADR = fctrl >> 7;
  361. packet->BODY.MACPayload.FHDR.FCtrl.uplink.ACK = (fctrl & (1 << 5)) >> 5;
  362. packet->BODY.MACPayload.FHDR.FCtrl.downlink.FPending = (fctrl & (1 << 4)) >> 4;
  363. packet->BODY.MACPayload.FHDR.FCtrl.uplink.FOptsLen = (fctrl & 0x0f);
  364. }
  365. uint16_t currFcnt32_LSB = (uint16_t) currFcnt32;
  366. uint16_t currFcnt32_MSB = (uint16_t) (currFcnt32 >> 16);
  367. if (packet->BODY.MACPayload.FHDR.FCnt16 < currFcnt32_LSB) {
  368. currFcnt32_MSB++;
  369. }
  370. lw_key.fcnt32 = (((uint32_t) currFcnt32_MSB) << 16) + packet->BODY.MACPayload.FHDR.FCnt16;
  371. // calc & compare mic
  372. packet->MIC = parseUInt32LittleEndian(dataToParse + length - 4);
  373. lw_msg_mic(&micCalc, &lw_key);
  374. if (micCalc.data != packet->MIC) { // check if mic is ok
  375. lib.api.LogError("Data %s MIC error! -> discarding incoming packet\n", uplink ? "uplink" : "downlink");
  376. lib.api.free(packet);
  377. return NULL;
  378. }
  379. memcpy(packet->BODY.MACPayload.FHDR.FOpts, &(dataToParse[idx]), packet->BODY.MACPayload.FHDR.FCtrl.uplink.FOptsLen);
  380. idx += packet->BODY.MACPayload.FHDR.FCtrl.uplink.FOptsLen;
  381. // copy other fields & decrypt payload (if present)
  382. uint8_t lengthWithoutPayloadAndPort = (1 + 7 + packet->BODY.MACPayload.FHDR.FCtrl.uplink.FOptsLen + 4); // MHDR(1) + FHDR(7) + FHDR_OPTS(x) + MIC (4)
  383. if (length > lengthWithoutPayloadAndPort) {
  384. packet->BODY.MACPayload.FPort = dataToParse[idx++];
  385. if (length == lengthWithoutPayloadAndPort + 1) { // no payload, but port
  386. packet->BODY.MACPayload.payloadLength = 0;
  387. lib.api.LogError("Lobawan: warn packet with port but without payload\n");
  388. } else {
  389. packet->BODY.MACPayload.payloadLength = length - 4 - idx;
  390. if (packet->BODY.MACPayload.FPort == 0) {
  391. lw_key.aeskey = lib.state.pDevCfg->nwkskey;
  392. } else {
  393. lw_key.aeskey = lib.state.pDevCfg->appskey;
  394. }
  395. lw_key.in = &(dataToParse[idx]);
  396. lw_key.len = packet->BODY.MACPayload.payloadLength;
  397. packet->pPayload = (uint8_t*) lib.api.malloc(packet->BODY.MACPayload.payloadLength);
  398. if (packet->pPayload == NULL) {
  399. lib.api.LogError("LoRaWAN_UnmarshalPacket failed -> out of memory!\n");
  400. lib.api.free(packet);
  401. return NULL;
  402. }
  403. // decrypt by encrypt
  404. if (lw_encrypt(packet->pPayload, &lw_key) <= 0) {
  405. lib.api.LogError("LoRaWAN_UnmarshalPacket decrypt fail\n");
  406. lib.api.free(packet->pPayload);
  407. lib.api.free(packet);
  408. return NULL;
  409. }
  410. }
  411. } else { // no payload, no port, no cry
  412. packet->BODY.MACPayload.payloadLength = 0;
  413. packet->pPayload = NULL;
  414. }
  415. return packet;
  416. case MTYPE_JOIN_ACCEPT:
  417. // MHDR(1) + [sizeof(JoinAccept_t)(12) + optional CFlist(16)] + MIC(4), max len: 33 byte
  418. if (length == 17) {
  419. packet->BODY.JoinAccept.hasCFlist = false;
  420. } else if (length == 17 + 16) {
  421. packet->BODY.JoinAccept.hasCFlist = true; // optional frequency list send by network server
  422. } else {
  423. lib.api.LogError("Lobawan: Got JoinRequest with unexspected length -> discarding incoming packet\n");
  424. lib.api.free(packet);
  425. return NULL;
  426. }
  427. // (1) beside MHDR whole the message is encrypted -> decrypt it first
  428. uint8_t decryptedData[33]; // temp buffer
  429. lw_key.aeskey = lib.state.pDevCfg->appkey;
  430. lw_key.in = dataToParse + 1; // skip MHDR
  431. lw_key.len = length - 1;
  432. decryptedData[0] = dataToParse[0]; // MHDR can be copied as it's not encrypted
  433. int pl_len = lw_join_decrypt(decryptedData + 1, &lw_key);
  434. if (pl_len <= 0) {
  435. lib.api.LogError("Lobawan: Can't decrypt JoinRequest\n");
  436. lib.api.free(packet);
  437. return NULL;
  438. }
  439. // (2) check MIC
  440. packet->MIC = parseUInt32LittleEndian(decryptedData + length - 4);
  441. lw_key.aeskey = lib.state.pDevCfg->appkey;
  442. lw_key.in = decryptedData;
  443. lw_key.len = length - 4; // skip MIC
  444. lw_join_mic(&micCalc, &lw_key);
  445. if (micCalc.data != packet->MIC) { // check if mic is ok
  446. lib.api.LogError("Join accept mic error -> discarding incoming packet\n");
  447. lib.api.free(packet);
  448. return NULL;
  449. }
  450. // (3) parse fields
  451. idx = 1; // skip already parsed MHDR
  452. packet->BODY.JoinAccept.JoinNonce = decryptedData[idx++];
  453. packet->BODY.JoinAccept.JoinNonce |= ((uint32_t) decryptedData[idx++] << 8);
  454. packet->BODY.JoinAccept.JoinNonce |= ((uint32_t) decryptedData[idx++] << 16);
  455. packet->BODY.JoinAccept.HomeNetID = decryptedData[idx++];
  456. packet->BODY.JoinAccept.HomeNetID |= ((uint32_t) decryptedData[idx++] << 8);
  457. packet->BODY.JoinAccept.HomeNetID |= ((uint32_t) decryptedData[idx++] << 16);
  458. packet->BODY.JoinAccept.DevAddr = parseUInt32LittleEndian(&(decryptedData[idx]));
  459. idx += 4;
  460. packet->BODY.JoinAccept.DLsettings.OptNeg = ((decryptedData[idx] & 0x80) >> 7);
  461. packet->BODY.JoinAccept.DLsettings.Rx1DRoffset = ((decryptedData[idx] & 0x70) >> 4);
  462. packet->BODY.JoinAccept.DLsettings.Rx2DR = (decryptedData[idx] & 0x0f);
  463. idx++;
  464. packet->BODY.JoinAccept.RxDelay = decryptedData[idx++];
  465. if (packet->BODY.JoinAccept.hasCFlist) {
  466. memcpy(packet->BODY.JoinAccept.CFlist.FreqCH4, decryptedData + idx, 3);
  467. memcpy(packet->BODY.JoinAccept.CFlist.FreqCH5, decryptedData + idx + 3, 3);
  468. memcpy(packet->BODY.JoinAccept.CFlist.FreqCH6, decryptedData + idx + 6, 3);
  469. memcpy(packet->BODY.JoinAccept.CFlist.FreqCH7, decryptedData + idx + 9, 3);
  470. memcpy(packet->BODY.JoinAccept.CFlist.FreqCH8, decryptedData + idx + 12, 3);
  471. }
  472. // (4) derive keys
  473. lw_skey_seed_t lw_skey_seed;
  474. lw_skey_seed.aeskey = lib.state.pDevCfg->appkey;
  475. lw_skey_seed.anonce.data = packet->BODY.JoinAccept.JoinNonce;
  476. lw_skey_seed.netid.data = packet->BODY.JoinAccept.HomeNetID;
  477. lw_skey_seed.dnonce.data = lib.state.pDevCfg->devnonce;
  478. lw_get_skeys(packet->BODY.JoinAccept.derived_nwkskey, packet->BODY.JoinAccept.derived_appskey, &lw_skey_seed); // todo maybe add as special "payload" to packet?
  479. // app should adjust nwkskey, appskey, devAdr, netId, appnounce
  480. return packet;
  481. case MTYPE_JOIN_REQUEST:
  482. // normally NOT needed to parse by a lorawan device but included for completeness (Unmarshalled by network servers only!)
  483. if (length != 23) { // MHDR(1) + [APPEUI(8) + DEVEUI(8) + DEVNOUNCE(2)] + MIC(4)
  484. lib.api.LogError("Lobawan: Got JoinRequest with unexspected length -> discarding incoming packet\n");
  485. lib.api.free(packet);
  486. return NULL;
  487. }
  488. packet->MIC = parseUInt32LittleEndian(dataToParse + length - 4);
  489. lw_key.aeskey = lib.state.pDevCfg->appkey;
  490. lw_key.in = dataToParse;
  491. lw_key.len = length - 4;
  492. lw_join_mic(&micCalc, &lw_key);
  493. // check if mic is ok
  494. if (micCalc.data != packet->MIC) {
  495. lib.api.LogError("Join Request mic error -> discarding incoming packet\n");
  496. lib.api.free(packet);
  497. return NULL;
  498. }
  499. idx = 1; // skip already parsed MHDR
  500. memcpy(packet->BODY.JoinRequest.joinEUI, dataToParse + idx, 8);
  501. idx += 8;
  502. memcpy(packet->BODY.JoinRequest.devEUI, dataToParse + idx, 8);
  503. idx += 8;
  504. packet->BODY.JoinRequest.devnonce = parseUInt16LittleEndian(&(dataToParse[idx]));
  505. //idx += 2;
  506. return packet;
  507. default:
  508. lib.api.LogError("Lobawan: unknown MHDR type -> discarding incoming packet\n");
  509. lib.api.free(packet);
  510. return NULL;
  511. }
  512. return packet;
  513. }