UNPKG

lora-packet

Version:
259 lines 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.recalculateMIC = exports.verifyMIC = exports.calculateMIC = void 0; const LoraPacket_1 = require("./LoraPacket"); const util_1 = require("./util"); const aes_cmac_1 = require("aes-cmac"); // calculate MIC from payload function calculateMIC(payload, NwkSKey, //NwkSKey for DataUP/Down; SNwkSIntKey in data 1.1; SNwkSIntKey in Join 1.1 AppKey, //AppSKey for DataUP/Down; FNwkSIntKey in data 1.1; JSIntKey in Join 1.1 FCntMSBytes, ConfFCntDownTxDrTxCh) { let LWVersion = LoraPacket_1.LorawanVersion.V1_0; if (payload.isJoinRequestMessage()) { if (AppKey && AppKey.length !== 16) throw new Error("Expected a AppKey with length 16"); if (!payload.MHDR) throw new Error("Expected MHDR to be defined"); if (!payload.AppEUI) throw new Error("Expected AppEUI to be defined"); if (!payload.DevEUI) throw new Error("Expected DevEUI to be defined"); if (!payload.DevNonce) throw new Error("Expected DevNonce to be defined"); if (!payload.MACPayload) throw new Error("Expected DevNonce to be defined"); // const msgLen = payload.MHDR.length + payload.AppEUI.length + payload.DevEUI.length + payload.DevNonce.length; // CMAC over MHDR | AppEUI | DevEUI | DevNonce // the seperate fields are not in little-endian format, use the concatenated field const cmacInput = Buffer.concat([payload.MHDR, payload.MACPayload]); // CMAC calculation (as RFC4493) let fullCmac = new aes_cmac_1.AesCmac(AppKey).calculate(cmacInput); if (!(fullCmac instanceof Buffer)) fullCmac = Buffer.from(fullCmac); // only first 4 bytes of CMAC are used as MIC const MIC = fullCmac.slice(0, 4); return MIC; } else if (payload.isReJoinRequestMessage()) { if (payload.RejoinType[0] === 1 && (!AppKey || AppKey.length !== 16)) throw new Error("Expected a JSIntKey with length 16"); if ((payload.RejoinType[0] === 0 || payload.RejoinType[0] === 2) && (!NwkSKey || NwkSKey.length !== 16)) throw new Error("Expected a SNwkSIntKey with length 16"); if (AppKey && AppKey.length !== 16) throw new Error("Expected a AppKey with length 16"); if (!payload.MHDR) throw new Error("Expected MHDR to be defined"); if (!payload.RejoinType) throw new Error("Expected RejoinType to be defined"); if (!payload.NetID && !payload.AppEUI) throw new Error("Expected NetID or JoinEUI to be defined"); if (!payload.DevEUI) throw new Error("Expected DevEUI to be defined"); if (!payload.RJCount0 && !payload.RJCount1) throw new Error("Expected RJCount0 or RJCount1 to be defined"); // const msgLen = payload.MHDR.length + payload.AppEUI.length + payload.DevEUI.length + payload.DevNonce.length; // CMAC over MHDR | AppEUI | DevEUI | DevNonce // the seperate fields are not in little-endian format, use the concatenated field const cmacInput = Buffer.concat([payload.MHDR, payload.MACPayload]); // CMAC calculation (as RFC4493) const calcKey = payload.RejoinType[0] === 1 ? AppKey : NwkSKey; let fullCmac = new aes_cmac_1.AesCmac(calcKey).calculate(cmacInput); if (!(fullCmac instanceof Buffer)) fullCmac = Buffer.from(fullCmac); // only first 4 bytes of CMAC are used as MIC const MIC = fullCmac.slice(0, 4); return MIC; } else if (payload.isJoinAcceptMessage()) { if (AppKey && AppKey.length !== 16) throw new Error("Expected a AppKey with length 16"); if (!payload.MHDR) throw new Error("Expected MHDR to be defined"); if (!payload.AppNonce) throw new Error("Expected AppNonce to be defined"); if (!payload.NetID) throw new Error("Expected NetID to be defined"); if (!payload.DevAddr) throw new Error("Expected DevAddr to be defined"); if (!payload.DLSettings) throw new Error("Expected DLSettings to be defined"); if (!payload.RxDelay) throw new Error("Expected RxDelay to be defined"); if (!payload.CFList) throw new Error("Expected CFList to be defined"); if (!payload.MACPayload) throw new Error("Expected MACPayload to be defined"); if (payload.getDLSettingsOptNeg()) LWVersion = LoraPacket_1.LorawanVersion.V1_1; let cmacInput = Buffer.alloc(0); let cmacKey = AppKey; if (LWVersion === LoraPacket_1.LorawanVersion.V1_0) { // const msgLen = // payload.MHDR.length + // payload.AppNonce.length + // payload.NetID.length + // payload.DevAddr.length + // payload.DLSettings.length + // payload.RxDelay.length + // payload.CFList.length; // CMAC over MHDR | AppNonce | NetID | DevAddr | DLSettings | RxDelay | CFList // the seperate fields are not encrypted, use the encrypted concatenated field cmacInput = Buffer.concat([payload.MHDR, payload.MACPayload]); } else if (LWVersion === LoraPacket_1.LorawanVersion.V1_1) { if (!payload.JoinReqType) throw new Error("Expected JoinReqType to be defined"); if (!payload.JoinEUI) throw new Error("Expected JoinEUI to be defined"); if (!payload.DevNonce) throw new Error("Expected DevNonce to be defined"); if (!NwkSKey || NwkSKey.length !== 16) throw new Error("Expected a NwkSKey with length 16"); cmacKey = NwkSKey; cmacInput = Buffer.concat([ payload.JoinReqType, (0, util_1.reverseBuffer)(payload.JoinEUI), (0, util_1.reverseBuffer)(payload.DevNonce), payload.MHDR, payload.MACPayload, ]); } // CMAC calculation (as RFC4493) let fullCmac = new aes_cmac_1.AesCmac(cmacKey).calculate(cmacInput); if (!(fullCmac instanceof Buffer)) fullCmac = Buffer.from(fullCmac); // only first 4 bytes of CMAC are used as MIC const MIC = fullCmac.slice(0, 4); return MIC; } else { // ConfFCntDownTxDrTxCh = ConfFCntDownTxDrTxCh || Buffer.alloc(2, 0); if (NwkSKey && NwkSKey.length !== 16) throw new Error("Expected a NwkSKey with length 16"); if (payload.DevAddr && payload.DevAddr.length !== 4) throw new Error("Expected a payload DevAddr with length 4"); if (payload.FCnt && payload.FCnt.length !== 2) throw new Error("Expected a payload FCnt with length 2"); if (!payload.MHDR) throw new Error("Expected MHDR to be defined"); if (!payload.DevAddr) throw new Error("Expected DevAddr to be defined"); if (!payload.FCnt) throw new Error("Expected FCnt to be defined"); if (!payload.MACPayload) throw new Error("Expected MACPayload to be defined"); if (!FCntMSBytes) { FCntMSBytes = Buffer.from("0000", "hex"); } if (ConfFCntDownTxDrTxCh) { if (!AppKey || (AppKey === null || AppKey === void 0 ? void 0 : AppKey.length) !== 16) throw new Error("Expected a FNwkSIntKey with length 16"); LWVersion = LoraPacket_1.LorawanVersion.V1_1; } // if (NwkSKey && AppKey) { // LWVersion = LorawanVersion.V1_1; // } let dir; const isUplinkAndIs1_1 = payload.getDir() === "up" && LWVersion === LoraPacket_1.LorawanVersion.V1_1; const isDownlinkAndIs1_1 = payload.getDir() === "down" && LWVersion === LoraPacket_1.LorawanVersion.V1_1; if (payload.getDir() == "up") { dir = Buffer.alloc(1, 0); } else if (payload.getDir() == "down") { dir = Buffer.alloc(1, 1); if (!ConfFCntDownTxDrTxCh) { ConfFCntDownTxDrTxCh = Buffer.alloc(4, 0); } else if (ConfFCntDownTxDrTxCh && (ConfFCntDownTxDrTxCh === null || ConfFCntDownTxDrTxCh === void 0 ? void 0 : ConfFCntDownTxDrTxCh.length) !== 2) { throw new Error("Expected a ConfFCntDown with length 2"); } else { ConfFCntDownTxDrTxCh = Buffer.concat([ConfFCntDownTxDrTxCh, Buffer.alloc(2, 0)]); } } else { throw new Error("expecting direction to be either 'up' or 'down'"); } if (isUplinkAndIs1_1) { if (!ConfFCntDownTxDrTxCh || (ConfFCntDownTxDrTxCh === null || ConfFCntDownTxDrTxCh === void 0 ? void 0 : ConfFCntDownTxDrTxCh.length) !== 4) { throw new Error("Expected a ConfFCntDownTxDrTxCh with length 4 Expected ( ConfFCnt | TxDr | TxCh)"); } if (payload.getFCtrlACK() || (isUplinkAndIs1_1 && payload.getFPort() === 0)) { ConfFCntDownTxDrTxCh.writeUInt16BE(ConfFCntDownTxDrTxCh.readUInt16LE(0)); } else { ConfFCntDownTxDrTxCh.writeUInt16BE(0); } } const msgLen = payload.MHDR.length + payload.MACPayload.length; const B0 = Buffer.concat([ Buffer.from([0x49]), isDownlinkAndIs1_1 ? ConfFCntDownTxDrTxCh : Buffer.alloc(4, 0), dir, (0, util_1.reverseBuffer)(payload.DevAddr), (0, util_1.reverseBuffer)(payload.FCnt), FCntMSBytes, Buffer.alloc(1, 0), Buffer.alloc(1, msgLen), // len(msg) ]); // CMAC over B0 | MHDR | MACPayload const cmacInput = Buffer.concat([B0, payload.MHDR, payload.MACPayload]); // CMAC calculation (as RFC4493) let key = NwkSKey; if (isDownlinkAndIs1_1) key = AppKey; let fullCmac = new aes_cmac_1.AesCmac(key).calculate(cmacInput); if (!(fullCmac instanceof Buffer)) fullCmac = Buffer.from(fullCmac); // only first 4 bytes of CMAC are used as MIC const MIC = fullCmac.slice(0, 4); if (isUplinkAndIs1_1) { const B1 = Buffer.concat([ Buffer.from([0x49]), ConfFCntDownTxDrTxCh, dir, (0, util_1.reverseBuffer)(payload.DevAddr), (0, util_1.reverseBuffer)(payload.FCnt), FCntMSBytes, Buffer.alloc(1, 0), Buffer.alloc(1, msgLen), // len(msg) ]); const cmacSInput = Buffer.concat([B1, payload.MHDR, payload.MACPayload]); let fullCmacS = new aes_cmac_1.AesCmac(AppKey).calculate(cmacSInput); if (!(fullCmacS instanceof Buffer)) fullCmacS = Buffer.from(fullCmacS); // only first 2 bytes of CMAC and CMACS are used as MIC const MICS = fullCmacS.slice(0, 4); return Buffer.concat([MICS.slice(0, 2), MIC.slice(0, 2)]); } return MIC; } } exports.calculateMIC = calculateMIC; // verify is just calculate & compare function verifyMIC(payload, NwkSKey, AppKey, FCntMSBytes, ConfFCntDownTxDrTxCh) { if (payload.MIC && payload.MIC.length !== 4) throw new Error("Expected a payload payload.MIC with length 4"); const calculated = calculateMIC(payload, NwkSKey, AppKey, FCntMSBytes, ConfFCntDownTxDrTxCh); if (!payload.MIC) return false; return Buffer.compare(payload.MIC, calculated) === 0; } exports.verifyMIC = verifyMIC; // calculate MIC & store function recalculateMIC(payload, NwkSKey, AppKey, FCntMSBytes, ConfFCntDownTxDrTxCh) { const calculated = calculateMIC(payload, NwkSKey, AppKey, FCntMSBytes, ConfFCntDownTxDrTxCh); payload.MIC = calculated; if (!payload.MHDR) throw new Error("Missing MHDR"); if (!payload.MACPayload) throw new Error("Missing MACPayload"); if (!payload.MIC) throw new Error("Missing MIC"); if (!payload.MHDR) throw new Error("Missing MHDR"); payload.PHYPayload = Buffer.concat([payload.MHDR, payload.MACPayload, payload.MIC]); payload.MACPayloadWithMIC = payload.PHYPayload.slice(payload.MHDR.length, payload.PHYPayload.length); } exports.recalculateMIC = recalculateMIC; //# sourceMappingURL=mic.js.map