{"id":13673745,"url":"https://github.com/anthonykirby/lora-packet","last_synced_at":"2025-04-28T11:30:45.618Z","repository":{"id":10080882,"uuid":"63622034","full_name":"anthonykirby/lora-packet","owner":"anthonykirby","description":"LoRa radio packet decoder","archived":false,"fork":false,"pushed_at":"2024-06-24T22:16:47.000Z","size":7388,"stargazers_count":269,"open_issues_count":12,"forks_count":83,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-04-10T01:59:10.064Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anthonykirby.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-07-18T17:11:03.000Z","updated_at":"2025-03-25T07:08:01.000Z","dependencies_parsed_at":"2023-11-09T00:37:23.785Z","dependency_job_id":"650f7bc7-b205-44d9-92c5-bf71af323fae","html_url":"https://github.com/anthonykirby/lora-packet","commit_stats":{"total_commits":132,"total_committers":12,"mean_commits":11.0,"dds":0.4696969696969697,"last_synced_commit":"a7866023fc1d04ad653b7ea4dfa91262ec2b48b5"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthonykirby%2Flora-packet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthonykirby%2Flora-packet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthonykirby%2Flora-packet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthonykirby%2Flora-packet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anthonykirby","download_url":"https://codeload.github.com/anthonykirby/lora-packet/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251304674,"owners_count":21567919,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T11:00:21.029Z","updated_at":"2025-04-28T11:30:40.602Z","avatar_url":"https://github.com/anthonykirby.png","language":"TypeScript","funding_links":[],"categories":["Tools"],"sub_categories":[],"readme":"# lora-packet\n\nA pure [node.js](http://nodejs.org/) library to decode and encode packets\nfor LoRa/LoRaWAN\u003csup\u003eTM\u003c/sup\u003e radio communication, based on the specification\nfrom the [LoRa Alliance](https://www.lora-alliance.org/) (based on V1.0.2 final), and as used by [The Things Network](https://www.thethingsnetwork.org/).\n\nPacket decoding is also wrapped in a simple command-line tool that accepts input in hex and base-64\n\n## Why?\n\n- LoRa packets are encrypted at the radio link level. They could be\n  decrypted at the radio receiver, but frequently they're transferred onwards\n  as-is, because the radio doesn't have the crypto keys. This library lets you\n  handle them in your code, rather than relying on less transparent / less\n  documented / less convenient libraries / modules / systems.\n- as a debugging tool, to check and decrypt packets\n- node.js is available both on the application server, and can also be\n  available on network gateways (which are otherwise hard to write code to\n  run on)- a single library can be used in both places / either place\n- inverted use case: you have a remote gateway, and you want to send gateway\n  telemetry/monitoring using the same uplink channel as used by the radio, as\n  LoRa packets - so you encode your gateway telemetry as LoRa packets \u0026 slip\n  them into the uplink.\n\n## Help me to help you: Give me data!\n\nI'm happy to fix or add functionality, but I can only do this if I have\nexample packets.\n\n## Features:\n\n- LoRa packet parsing \u0026 analysis\n- MIC (Message Integrity Check) checking\n- payload decryption\n- decodes uplink \u0026 downlink packets, network join etc\n- ability to create LoRa format packets\n\n## Installation\n\n(nodejs\u003e=10)\n\n```bash\nnpm install lora-packet\n```\n\n(nodejs\u003c=9)\n\n```bash\nnpm install lora-packet@~0.7.14\n```\n\n## Usage (command-line packet decoding):\n\n```\n$ lora-packet-decode --hex 40F17DBE4900020001954378762B11FF0D\n```\n\n```\n$ lora-packet-decode --base64 QPF9vkkAAgABlUN4disR/w0=\n```\n\n```\n$ lora-packet-decode \\\n        --appkey ec925802ae430ca77fd3dd73cb2cc588 \\\n        --nwkkey 44024241ed4ce9a68c6a8bc055233fd3 \\\n        --hex 40F17DBE4900020001954378762B11FF0D\n```\n\n## Usage (packet decoding from wire):\n\n### fromWire(buffer)\n\nParse \u0026 create packet structure from wire-format buffer (i.e. \"radio PHYPayload\")\n\n### packet.getBuffers()\n\nreturns an object containing the decoded packet fields, named as per\nLoRa spec, e.g. _MHDR_, _MACPayload_ etc\n\nNote: _DevAddr_ and _FCnt_ are stored big-endian, i.e. the way round\nthat you'd expect to see them, not how they're sent down the wire.\n\n### packet.getMType()\n\nreturns the packet _MType_ as a string (e.g. \"Unconfirmed Data Up\")\n\n### packet.getDir()\n\nreturns the direction (_Dir_) as a string ('up' or 'down')\n\n### packet.getFCnt()\n\nreturns the frame count (_FCnt_) as a number\n\n### packet.isConfirmed()\n\nreturns true if packet is confirmed, else returns false \n\n### packet.getFPort()\n\nreturns the port (_FPort_) as a number (or null if FPort is absent)\n\n### packet.getFCtrlACK()\n\nreturns the flag (_ACK_) of field _FCtrl_ as a boolean\n\n### packet.getFCtrlFPending()\n\nreturns the flag (_FPending_) of field _FCtrl_ as a boolean\n\n### packet.getFCtrlADR()\n\nreturns the flag (_ADR_) of field _FCtrl_ as a boolean\n\n### packet.getFCtrlADRACKReq()\n\nreturns the flag (_ADRACKReq_) of field _FCtrl_ as a boolean\n\n### packet.encryptFOpts(NwkSEncKey, [, SNwkSIntKey] [, FCntMSBytes] [, ConfFCntDownTxDrTxCh])\nreturns an object containing the encrypted FOpts field. \nIf SNwkSIntKey is provided, the mic is recalculated and modifies the packet.\n\n### packet.decryptFOpts(NwkSEncKey, [, SNwkSIntKey] [, FCntMSBytes] [, ConfFCntDownTxDrTxCh])\nalias for encryptFOpts, just for sake of clarification\n\n### verifyMIC(packet, NwkSKey [, AppKey] [, FCntMSBytes])\n\nreturns a boolean; true if the MIC is correct (i.e. the value at the end of\nthe packet data matches the calculation over the packet contents)\n\nNB AppKey is used for Join Request/Accept, otherwise NwkSkey is used\n\nOptionally, if using 32-byt FCnts, supply the upper 2 bytes as a Buffer.\n\n### calculateMIC(packet, NwkSKey [, AppKey] [, FCntMSBytes])\n\nreturns the MIC, as a buffer\n\nNB AppKey is used for Join Request/Accept, otherwise NwkSkey is used\n\nOptionally, if using 32-byt FCnts, supply the upper 2 bytes as a Buffer.\n\n### recalculateMIC(packet, NwkSKey [, AppKey] [, FCntMSBytes])\n\ncalculates the MIC \u0026 updates the packet (no return value)\n\nNB AppKey is used for Join Request/Accept, otherwise NwkSkey is used\n\nOptionally, if using 32-byt FCnts, supply the upper 2 bytes as a Buffer.\n\n### decrypt(packet, AppSKey, NwkSKey [, FCntMSBytes]\n\ndecrypts and returns the payload as a buffer:\nThe library cannot know whether this is an ASCII string or binary data,\nso you will need to interpret it appropriately.\n\nNB the relevant key is chosen depending on the value of _FPort_,\nand NB key order is different than MIC APIs\n\n### decryptJoinAccept(inputData, appKey)\n\ndecrypts and returns the Join Accept Message as a buffer:\n\n```javascript\nconst packet = lora_packet.fromWire(inputData);\nconst DecryptedPacket = lora_packet.fromWire(lora_packet.decryptJoinAccept(packet, appKey));\n```\n\n## Usage (packet encoding to wire):\n\n### fromFields(data)\n\ntakes an object with properties representing fields in the packet - see example below\n\n- and generates a valid packet from them. If a NwkSKey is provided then the\n  MIC is calculated (otherwise = \"EEEEEEEE\") and if the relevant encryption key\n  (AppSKey or NwkSKey depending on port) then the payload is encrypted.\n\nThe wire-format payload can be obtained by calling _getPHYPayload()_\n(or _getBuffers().PHYPayload_)\n\n\n#### Required fields:\n\n- _MType_ - supplied as number (0-7 or constants) or string\n- _DevAddr_ - supplied as Buffer (4)\n- _FCnt_ - supplied as number or Buffer(2)\n\n#### Optional fields:\n\n- _FCtrl.ADR_ - boolean (default = false)\n- _FCtrl.ADRACKReq_ - boolean (default = false)\n- _FCtrl.ACK_ - boolean (default = false)\n- _FCtrl.FPending_ - boolean (default = false)\n- _FPort_ - number (default = 1)\n\n#### Lorawan 1.1\n in Lorawan 1.1 optional fields after `data` are:\n- NwkSEncKey,\n- SNwkSIntKey,\n- FNwkSIntKey,\n- FCntMSBytes\n- ConfFCntDownTxDrTxCh\n\nFor usage Refer to Lorawan 1.1 Spec.\n\n## Example:\n\n```javascript\nconst lora_packet = require(\"lora-packet\");\n\n//-----------------\n// packet decoding\n\n// decode a packet\nconst packet = lora_packet.fromWire(Buffer.from(\"40F17DBE4900020001954378762B11FF0D\", \"hex\"));\n\n// debug: prints out contents\n// - contents depend on packet type\n// - contents are named based on LoRa spec\nconsole.log(\"packet.toString()=\\n\" + packet);\n\n// e.g. retrieve payload elements\nconsole.log(\"packet MIC=\" + packet.MIC.toString(\"hex\"));\nconsole.log(\"FRMPayload=\" + packet.FRMPayload.toString(\"hex\"));\n\n// check MIC\nconst NwkSKey = Buffer.from(\"44024241ed4ce9a68c6a8bc055233fd3\", \"hex\");\nconsole.log(\"MIC check=\" + (lora_packet.verifyMIC(packet, NwkSKey) ? \"OK\" : \"fail\"));\n\n// calculate MIC based on contents\nconsole.log(\"calculated MIC=\" + lora_packet.calculateMIC(packet, NwkSKey).toString(\"hex\"));\n\n// decrypt payload\nconst AppSKey = Buffer.from(\"ec925802ae430ca77fd3dd73cb2cc588\", \"hex\");\nconsole.log(\"Decrypted (ASCII)='\" + lora_packet.decrypt(packet, AppSKey, NwkSKey).toString() + \"'\");\nconsole.log(\"Decrypted (hex)='0x\" + lora_packet.decrypt(packet, AppSKey, NwkSKey).toString(\"hex\") + \"'\");\n\n//-----------------\n// packet creation\n\n// create a packet\nconst constructedPacket = lora_packet.fromFields(\n  {\n    MType: \"Unconfirmed Data Up\", // (default)\n    DevAddr: Buffer.from(\"01020304\", \"hex\"), // big-endian\n    FCtrl: {\n      ADR: false, // default = false\n      ACK: true, // default = false\n      ADRACKReq: false, // default = false\n      FPending: false, // default = false\n    },\n    FCnt: Buffer.from(\"0003\", \"hex\"), // can supply a buffer or a number\n    payload: \"test\",\n  },\n  Buffer.from(\"ec925802ae430ca77fd3dd73cb2cc588\", \"hex\"), // AppSKey\n  Buffer.from(\"44024241ed4ce9a68c6a8bc055233fd3\", \"hex\") // NwkSKey\n);\nconsole.log(\"constructedPacket.toString()=\\n\" + constructedPacket);\nconst wireFormatPacket = constructedPacket.getPHYPayload();\nconsole.log(\"wireFormatPacket.toString()=\\n\" + wireFormatPacket.toString(\"hex\"));\n```\n\n\n## Notes:\n\n#### Online Decoder\n\nThere's a nice [online decoder that uses this library](https://runkit.io/avbentem/lorawan-packet-decoder/branches/master).\n\nNB this is created \u0026 maintained by a third party \u0026 I can't support or answer questions about it.\n\n#### Endianness\n\n- LoRa sends data over the wire in little-endian format\n  (see spec #1.2 \"The octet order for all multi-­octet fields is little endian\")\n- lora-packet attempts to hide this from you, so e.g. DevAddr \u0026 FCnt are\n  presented in big-endian format.\n- For example, DevAddr=49be7df1 is sent over the wire as 0xf1, 0x7d, 0xbe, 0x49.\n- Similarly, the fields in the Join Request message (AppEUI, DevEUI, DevNonce)\n  are reversed on the wire\n\n#### Can I help?\n\n- I've done some testing, but of course I can only test using the packets\n  that I can generate \u0026 receive with the radios I've got, and packets I've\n  constructed myself. If you find a packet that `lora-packet` fails to parse,\n  or incorrectly decodes / decrypts etc, please let me know!\n\n#### LoRaWAN - naming clarification\n\nIt took me longer than expected to understand the various IDs \u0026 key names.\nDifferent terminology is used by LoRaWAN / TTN / Multitech, \u0026 there's both\nOTA \u0026 manual personalisation options. This is a quick summary which I hope\nyou'll find helpful.\n\n(TODO!)\n\n#### Version history\n\n- 0.9.2 bump ws to 7.5.10, braces to 3.0.3:  Drop test support for node.js \\\u003e 16\n- 0.9.1 remove Buffer dependency, and support more Node versions\n- 0.9.0 refactor to improve readabilty and modularity\n- 0.8.25\n- - sanity checks in fromWire\n- - fix parsing RejoinRequest\n- - add toString output for RejoinRequest\n- 0.8.24 bump crypto-js to 4.2.0 (CVE-2023-46233)\n- 0.8.23 bump @babel/traverse to 7.23.2\n- 0.8.22 add WOR keys\n- 0.8.21 bump semver\n- 0.8.20 bump tough-cookie to 4.1.3, word-wrap to 1.2.4\n- 0.8.19 bump decode-uri-component 0.2.2, json5 2.2.3 (CVE–2022–46175)\n- 0.8.18 bump minimatch 3.1.2 (CVE-2022-3517)\n- 0.8.17 add support for LoRaWAN 1.1\n- 0.8.15 bump minimist 1.2.6 (CVE-2021-44906/CVE-2020-7598)\n- 0.8.14 migrate from node-aes-cmac to aes-cmac\n- 0.8.13 parse RFU and proprietary packets\n- 0.8.12 bump multiple deps\n- 0.8.11 bump glob-parent 5.1.2 (CVE-2020-28469)\n- 0.8.10 bump ws to 7.4.6 (CVE-2021-32640)\n- 0.8.9 bump lodash to 4.17.21 (CVE-2021-23337)\n- 0.8.8 bump y18n to 4.0.1 (CVE-2020-7774)\n- 0.8.7 fix recalculateMIC\n- 0.8.6 add isConfirmed \u0026 fix initialise with Port=0\n- 0.8.5 add docs + text output for FPending (data down) + ADRACKReq (data up)\n- 0.8.3 default FCnt should be 0\n- 0.8.2 fix decryption of Join Accept\n- 0.8.1 fix shebang\n- 0.8.0 upgrade to typescript \u0026 node 10.x/12.x/14.x; deprecate pre-10.x\n- 0.7.14 bump mocha version\n- 0.7.13 fix CFList length\n- 0.7.12 fix CFList byte order\n- 0.7.10 add Decrypt Join Accept\n- 0.7.8 improve support for 32-bit FCnt\n- 0.7.7 add command-line support for AppSKey/NwkSKey\n- 0.7.4 add support for 32-bit FCnt in MIC calculation\n- 0.7.2 fix Join Accept parsing\n- 0.7.0 add support for join packets and OTAA handshaking\n- 0.6.0 when creating a packet from fields, if no FPort and no payload are specified, omit FPort\n- 0.5.4 command-line behaves gracefully on no input\n- 0.5.3 MIC for join messages; getter for FCtrl.ADRACKReq\n- 0.5.2 fix FOpts parsing\n- 0.5.0 add command-line tool\n- 0.4.0 implemented creation of packet (+ MIC + encryption) from payload / fields\n- 0.3.0 refactor to allow packet creation\n- 0.2.0 initial release as npm\n\n\n#### TODO\n\n- MAC Commands, as sent in _FOpts_ (or piggybacked in _FRMPayload_)\n\n#### Credits\n\n- Thank you to [David Olivari](https://github.com/davidonet)\n- Thank you to [Larko](https://github.com/larkolab)\n- Thank you to [Tommas Bakker](https://github.com/tommas-factorylab)\n- Thank you to [Rob Gillan](https://github.com/rgillan)\n- Thank you to [Christopher Hunt](https://github.com/huntc)\n- Thank you to [Thibault Ortiz](https://github.com/tortizactility)\n- Thank you to [Flemming Madsen](https://github.com/amplexdenmark)\n- Thank you to [Giorgio Pillon](https://github.com/kalik1)\n- Thank you to [Nuno Cruz](https://github.com/nunomcruz)\n- Thank you to [Felipe Lima](https://github.com/felipefdl) and the fine folks at [TagoIO](https://tago.io/)\n- Thank you to [Nicolas Graziano](https://github.com/ngraziano)\n- Thank you to [Benjamin Cabé](https://github.com/kartben)\n- Thank you to [kalik1](https://github.com/kalik1)\n- Thank you to [Pierre PLR](https://github.com/pplr)\n- Thank you to [Ricardo Stoklosa](https://github.com/RicardoStoklosa)\n- Thank you to [Lucas](https://github.com/aqllmcdavid)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanthonykirby%2Flora-packet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanthonykirby%2Flora-packet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanthonykirby%2Flora-packet/lists"}