LoRaWAN packet decoder

node v6.17.1
version: 1.0.0
endpointsharetweet
This uses the nice lora-packet library to decode packets from LoRaWAN gateway logs. Just paste the packet into the first line below and hit Shift+Return to decode it.
// Copy the packet from, e.g., "data": "ADFGUkFEshgAdAoAAACyGADXQ5rzpZs=" into // the variable below and hit Shift+Return. Or use the API endpoint to see a // form, or pass the data in the REST URL. // const data = 'ADFGUkFEshgAdAoAAACyGADXQ5rzpZs='; // const nwkSKey = null; // const appSKey = null; const data = 'QK4TBCaAAAABb4ldmIEHFOMmgpU='; // Optional, to validate MIC and decrypt payload const nwkSKey = '99D58493D1205B43EFF938F0F66C339E'; const appSKey = '0A501524F8EA5FCBF9BDB5AD7D126F75'; const lorapacket = require('[email protected]'); function decode(data, nwkSKey, appSKey) { data = data.trim(); nwkSKey = nwkSKey ? new Buffer(nwkSKey.trim(), 'hex') : undefined; appSKey = appSKey ? new Buffer(appSKey.trim(), 'hex') : undefined; const enc = data.match(/^[0-9A-F]*$/i) ? 'hex' : 'base64'; try { const packet = lorapacket.fromWire(new Buffer(data, enc)); const isJoinAccept = packet.getMType() === 'Join Accept'; const isJoin = isJoinAccept || packet.getMType() === 'Join Request'; let decoded = packet.toString(); if(isJoinAccept) { decoded = decoded.replace('Join Accept', 'Join Accept -- <strong style="color: #f00">WARNING: The values below have not been decrypted</strong>'); } if(nwkSKey && appSKey) { const expected = lorapacket.calculateMIC(packet, nwkSKey, appSKey); const valid = lorapacket.verifyMIC(packet, nwkSKey, appSKey); decoded = decoded.replace(/^(.*MIC = .*$)/m, '$1 ' + (valid ? '(validated)' : '<strong style="color: #f00">INVALID; expected ' + expected.toString('hex').toUpperCase() + '</strong>')); if(!isJoin) { const payload = lorapacket.decrypt(packet, appSKey, nwkSKey); // We don't have to align the additional line here, as it will be re-aligned later decoded = decoded.replace(/^(.*FRMPayload) = .+$/m, (match, m1) => `${match} (encrypted)\n = ${payload.toString('hex').toUpperCase()} (decrypted)`); } } else { // decoded += '\nProvide AppSKey and NwkSKey to validate MIC and decrypt payload'; } // Align the output on the '=' character with as few leading whitespace as possible // (and fix an error in the output): const lines = decoded.split('\n'); const lengths = lines.map(s => s.replace(/^\s*(.*)( = .*)$/, (match, m1, m2) => m1).length); const max = Math.max(...lengths.filter(length => length > 0)); decoded = lines.map(s => s.replace(/^\s*(.*)( = .*)$/, (match, m1, m2) => ' '.repeat(max - m1.length) + m1 + m2)).join('\n'); return `Assuming ${enc}-encoded packet\n${data}\n\n${decoded}`; } catch(e) { return e.message; } } if(data) { decode(data, nwkSKey, appSKey); }
See & star https://github.com/anthonykirby/lora-packet. Or, if you want to install a command line tool utility: - install Node.js and npm; https://docs.npmjs.com/getting-started/installing-node - run: npm install -g lora-packet - run: lora-packet-decode --base64 ADFGUkFEshgAdAoAAACyGADXQ5rzpZs= - ...or: lora-packet-decode --hex 003146524144B21800740A000000B21800D7439AF3A59B For HEX, beware that removing leading zeroes will affect proper decoding!
// Same as above, for HTML form and API endpoint const express = require("@runkit/runkit/express-endpoint/1.0.0"); const app = express(exports); app.get("/", (req, res) => { // exports.endpoint = function(req, res) { let decoded = ""; if(req.query && req.query.data) { decoded = ` <pre>${decode(req.query.data, req.query.nwkskey, req.query.appskey)}</pre> `; } else { decoded = ` <p><strong>Examples</strong></p> <p>Copy the packet from, e.g., <code>"data": "ADFGUkFEshgAdAoAAACyGADXQ5rzpZs="</code> in the gateway's log.</p> <ul> <li><a href="?data=ANwAANB%2B1bNwHm/t9XzurwDIhgMK8sk=">Join Request <code>ANwAANB+1bNwHm/t9XzurwDIhgMK8sk=</code></a></li> <li><a href="?data=IIE/R/UI/6JnC24j4B%2BEueJdnEEV8C7qCz3T4gs%2BypLa">Join Accept <code>IIE/R/UI/6JnC24j4B+EueJdnEEV8C7qCz3T4gs+ypL</code></a></li> <li><a href="?data=QCkuASaAAAAByFaF53Iu%2BvzmwQ==">Uplink <code>QCkuASaAAAAByFaF53Iu+vzmwQ==</code></a></li> <li><a href="?data=QK4TBCaAAAABb4ldmIEHFOMmgpU=&nwkskey=99D58493D1205B43EFF938F0F66C339E&appskey=0A501524F8EA5FCBF9BDB5AD7D126F75">Uplink with secret keys<code>QK4TBCaAAAABb4ldmIEHFOMmgpU=</code></a></li> <li><a href="?data=YCkuASYgAwDQNni9">Downlink <code>YCkuASYgAwDQNni9</code></a></li> </ul> `; } res.setHeader("Content-Type", "text/html"); res.send(` <!doctype html> <html lang=en> <head> <meta charset=utf-8> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>LoRaWAN packet decoder</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-xs-12 col-sm-12 col-md-10 col-md-offset-1"> <h1>LoRaWAN packet decoder</h1> <p class="lead">A frontend towards <a href="https://github.com/anthonykirby/lora-packet">lora-packet</a>.</p> <form> <div class="row"> <div class="col-sm-10"> <label for="data">Base64 or hex-encoded packet ${req.query.data ? '(<a href="?">examples</a>)' : ''}</label> <input id="data" name="data" class="form-control input-lg" type="text" placeholder='Base64 or hex-encoded packet' value="${req.query.data ? req.query.data : ''}"> </div> <div class="hidden-xs col-sm-2"> <label for="submit">&nbsp;</label> <button id="submit" class="btn btn-default btn-lg col-sm-12" type="submit">Decode</button> </div> </div> <br> <div class="row"> <div class="col-sm-6"> <label for="nwkskey">Secret NwkSKey (hex-encoded; optional)</label> <input id="nwkskey" name="nwkskey" class="form-control input" type="text" placeholder='Network Session Key' value="${req.query.nwkskey ? req.query.nwkskey : ''}"> </div> <div class="col-sm-6"> <label for="appskey">Secret AppSKey (hex-encoded; optional)</label> <input id="appskey" name="appskey" class="form-control input" type="text" placeholder='Application Session Key' value="${req.query.appskey ? req.query.appskey : ''}"> </div> <div class="col-sm-12"> <p>Specify the secrets if you want to validate the MIC and decrypt the payload. Secrets are sent to the server and might be stored in log files of RunKit.</p> </div> </div> </form> <div class="row">&nbsp;</div> <div class="row"> <div class="col-xs-12"> ${decoded} </div> </div> <div class="row"> <div class="col-sm-12"> <p>The output above is the standard output of <a href="https://github.com/anthonykirby/lora-packet">lora-packet</a> with some minor enhancements if the secrets are known, to show if the MIC is valid and to show the decrypted payload.</p> <p>Beware that we're handling binary data, so do not remove leading zeroes. Also note that erroneous packets are likely to show incorrect results without any warning, so specify the secrets to validate the Message Integrity Code.</p> <p>OTAA Join Requests are not encrypted. OTAA Join Accepts are not fully supported above, as <a href="https://runkit.com/avbentem/deciphering-a-lorawan-otaa-join-accept">those need some additional data</a> to validate their MIC and derive the secret session keys.</p> <p>lora-packet can also be installed as a command line utility, but cannot validate/decrypt then:</p> <ul> <li><a href="https://docs.npmjs.com/getting-started/installing-node">install Node.js and npm</a></li> <li>run: <code>npm install -g lora-packet</code></li> <li>run: <code>lora-packet-decode --base64 ADFGUkFEshgAdAoAAACyGADXQ5rzpZs=</code><br/>or <code>lora-packet-decode --hex 003146524144B21800740A000000B21800D7439AF3A59B</code></li> </ul> <p>The maintainer of this page is not affiliated with lora-packet. See & star <a href="https://github.com/anthonykirby/lora-packet">https://github.com/anthonykirby/lora-packet</a>. </div> </div> </div> </div> </div> </body> </html> `); }); app.get("/:data", (req, res) => { res.setHeader("Content-Type", "text/plain"); res.send(decode(req.params.data) + "\nDecoded using https://github.com/anthonykirby/lora-packet"); }); // RunKit will print the last value; nothing to see here. let dummy = "";
Loading…

no comments

    sign in to comment