UNPKG

zwave-js

Version:

Z-Wave driver written entirely in JavaScript/TypeScript

312 lines (311 loc) 13.1 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var Security2_exports = {}; __export(Security2_exports, { Security2CCBehaviors: () => Security2CCBehaviors, Security2CCHooks: () => Security2CCHooks }); module.exports = __toCommonJS(Security2_exports); var import_cc = require("@zwave-js/cc"); var import_core = require("@zwave-js/core"); var import_testing = require("@zwave-js/testing"); var BootstrapStage; (function(BootstrapStage2) { BootstrapStage2[BootstrapStage2["Started"] = 0] = "Started"; BootstrapStage2[BootstrapStage2["Handshake"] = 1] = "Handshake"; BootstrapStage2[BootstrapStage2["RequestingKeys"] = 2] = "RequestingKeys"; BootstrapStage2[BootstrapStage2["VerifyingKeys"] = 3] = "VerifyingKeys"; BootstrapStage2[BootstrapStage2["Finalizing"] = 4] = "Finalizing"; })(BootstrapStage || (BootstrapStage = {})); const STATE_KEY_PREFIX = "Security2_"; const StateKeys = { bootstrapState: `${STATE_KEY_PREFIX}bootstrapState` }; const respondToS2KEXGet = { handleCC(controller, self, receivedCC) { const sm2Node = self.securityManagers.securityManager2; if (!sm2Node) return; if (receivedCC instanceof import_cc.Security2CCKEXGet) { self.state.set(StateKeys.bootstrapState, { stage: BootstrapStage.Started }); const cc = new import_cc.Security2CCKEXReport({ nodeId: controller.ownNodeId, echo: false, supportedKEXSchemes: [import_cc.KEXSchemes.KEXScheme1], supportedECDHProfiles: [import_cc.ECDHProfiles.Curve25519], requestCSA: false, requestedKeys: [...self.capabilities.securityClasses] }); return { action: "sendCC", cc }; } } }; const respondToS2KEXSet = { handleCC(controller, self, receivedCC) { const sm2Node = self.securityManagers.securityManager2; if (!sm2Node) return; if (receivedCC instanceof import_cc.Security2CCKEXSet && !receivedCC.echo) { self.state.set(StateKeys.bootstrapState, { stage: BootstrapStage.Handshake, grantedKeys: receivedCC.grantedKeys }); const cc = new import_cc.Security2CCPublicKeyReport({ nodeId: controller.ownNodeId, includingNode: false, publicKey: self.ecdhKeyPair.publicKey }); return { action: "sendCC", cc }; } } }; const respondToS2KEXReport = { async handleCC(controller, self, receivedCC) { const sm2Node = self.securityManagers.securityManager2; if (!sm2Node) return; if (receivedCC instanceof import_cc.Security2CCKEXReport && receivedCC.echo && receivedCC.isEncapsulatedWith(import_core.CommandClasses["Security 2"])) { const currentState = self.state.get(StateKeys.bootstrapState); if (currentState?.stage !== BootstrapStage.Handshake) return; self.state.set(StateKeys.bootstrapState, { stage: BootstrapStage.RequestingKeys, grantedKeys: currentState.grantedKeys, keys: /* @__PURE__ */ new Map() }); const firstKey = currentState.grantedKeys[0]; return requestKey(self, firstKey); } return void 0; } }; const handleS2PublicKeyReport = { async handleCC(controller, self, receivedCC) { const sm2Node = self.securityManagers.securityManager2; if (!sm2Node) return; if (receivedCC instanceof import_cc.Security2CCPublicKeyReport && receivedCC.includingNode) { const sharedSecret = await (0, import_core.deriveSharedECDHSecret)({ publicKey: receivedCC.publicKey, privateKey: self.ecdhKeyPair.privateKey }); const tempKeys = await (0, import_core.deriveTempKeys)(await (0, import_core.computePRK)(sharedSecret, receivedCC.publicKey, self.ecdhKeyPair.publicKey)); sm2Node.deleteNonce(controller.ownNodeId); sm2Node.tempKeys.set(controller.ownNodeId, { keyCCM: tempKeys.tempKeyCCM, personalizationString: tempKeys.tempPersonalizationString }); await requestNonce(controller, self); let cc = new import_cc.Security2CCKEXSet({ nodeId: controller.ownNodeId, echo: true, // TODO: We should copy these from the received KEX Set instead selectedKEXScheme: import_cc.KEXSchemes.KEXScheme1, selectedECDHProfile: import_cc.ECDHProfiles.Curve25519, permitCSA: false, grantedKeys: [...self.capabilities.securityClasses] }); cc = import_cc.Security2CC.encapsulate(cc, self.id, self.securityManagers); return { action: "sendCC", cc }; } } }; const handleS2NetworkKeyReport = { async handleCC(controller, self, receivedCC) { const sm2Node = self.securityManagers.securityManager2; if (!sm2Node) return; if (receivedCC instanceof import_cc.Security2CCNetworkKeyReport && receivedCC.isEncapsulatedWith(import_core.CommandClasses["Security 2"])) { const currentState = self.state.get(StateKeys.bootstrapState); if (currentState?.stage !== BootstrapStage.RequestingKeys) return; currentState.keys.set(receivedCC.grantedKey, receivedCC.networkKey); self.encodingContext.setSecurityClass(controller.ownNodeId, receivedCC.grantedKey, true); await sm2Node.setKey(receivedCC.grantedKey, receivedCC.networkKey); self.state.set(StateKeys.bootstrapState, { ...currentState, stage: BootstrapStage.VerifyingKeys, currentKey: receivedCC.grantedKey }); await requestNonce(controller, self); return verifyKey(self, receivedCC.grantedKey); } } }; async function requestNonce(controller, self) { const nonceGet = new import_cc.Security2CCNonceGet({ nodeId: controller.ownNodeId }); await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(nonceGet, { ackRequested: false })); const nonceReport = await self.expectControllerFrame((resp) => resp.type === import_testing.MockZWaveFrameType.Request && resp.payload instanceof import_cc.Security2CCNonceReport && resp.payload.SOS, { timeout: 1e3 }); return nonceReport.payload.receiverEI; } __name(requestNonce, "requestNonce"); function requestKey(self, secClass) { let networkKeyGet = new import_cc.Security2CCNetworkKeyGet({ nodeId: self.controller.ownNodeId, requestedKey: secClass }); networkKeyGet = import_cc.Security2CC.encapsulate(networkKeyGet, self.id, self.securityManagers); return { action: "sendCC", cc: networkKeyGet }; } __name(requestKey, "requestKey"); function verifyKey(self, secClass) { let networkKeyVerify = new import_cc.Security2CCNetworkKeyVerify({ nodeId: self.controller.ownNodeId }); networkKeyVerify = import_cc.Security2CC.encapsulate(networkKeyVerify, self.id, self.securityManagers, { securityClass: secClass }); return { action: "sendCC", cc: networkKeyVerify }; } __name(verifyKey, "verifyKey"); const handleS2TransferEnd = { async handleCC(controller, self, receivedCC) { const sm2Node = self.securityManagers.securityManager2; if (!sm2Node) return; if (receivedCC instanceof import_cc.Security2CCTransferEnd && receivedCC.keyVerified && !receivedCC.keyRequestComplete && receivedCC.isEncapsulatedWith(import_core.CommandClasses["Security 2"])) { const currentState = self.state.get(StateKeys.bootstrapState); if (currentState?.stage !== BootstrapStage.RequestingKeys) return; const nextKey = currentState.grantedKeys[currentState.keys.size]; if (nextKey) { return requestKey(self, nextKey); } currentState.stage = BootstrapStage.Finalizing; let transferEnd = new import_cc.Security2CCTransferEnd({ nodeId: controller.ownNodeId, keyVerified: false, keyRequestComplete: true }); transferEnd = import_cc.Security2CC.encapsulate(transferEnd, self.id, self.securityManagers); return { action: "sendCC", cc: transferEnd }; } } }; const respondToS2NonceGet = { async handleCC(controller, self, receivedCC) { const sm2Node = self.securityManagers.securityManager2; if (!sm2Node) return; if (receivedCC instanceof import_cc.Security2CCNonceGet) { const bootstrapState = self.state.get(StateKeys.bootstrapState); if (bootstrapState?.stage === BootstrapStage.VerifyingKeys) { self.encodingContext.setSecurityClass(controller.ownNodeId, bootstrapState.currentKey, false); sm2Node.deleteNonce(controller.ownNodeId); self.state.set(StateKeys.bootstrapState, { stage: BootstrapStage.RequestingKeys, grantedKeys: bootstrapState.grantedKeys, keys: bootstrapState.keys }); } else if (bootstrapState?.stage === BootstrapStage.Finalizing) { for (const [secClass, key] of bootstrapState.keys) { self.encodingContext.setSecurityClass(controller.ownNodeId, secClass, true); self.encodingContext.setSecurityClass(self.id, secClass, true); await sm2Node.setKey(secClass, key); sm2Node.tempKeys.delete(controller.ownNodeId); sm2Node.deleteNonce(controller.ownNodeId); } self.state.delete(StateKeys.bootstrapState); } const nonce = await sm2Node.generateNonce(controller.ownNodeId); const cc = new import_cc.Security2CCNonceReport({ nodeId: controller.ownNodeId, SOS: true, MOS: false, receiverEI: nonce }); return { action: "sendCC", cc }; } } }; const handleS2DecodeError = { async handleCC(controller, self, receivedCC) { const sm2Node = self.securityManagers.securityManager2; if (!sm2Node) return; if (receivedCC instanceof import_cc.InvalidCC) { if (receivedCC.reason === import_core.ZWaveErrorCodes.Security2CC_CannotDecode || receivedCC.reason === import_core.ZWaveErrorCodes.Security2CC_NoSPAN) { const nonce = await sm2Node.generateNonce(controller.ownNodeId); const cc = new import_cc.Security2CCNonceReport({ nodeId: controller.ownNodeId, SOS: true, MOS: false, receiverEI: nonce }); return { action: "sendCC", cc }; } } } }; const respondToS2CommandsSupportedGet = { handleCC(controller, self, receivedCC) { if (receivedCC instanceof import_cc.Security2CCCommandsSupportedGet) { const encapCC = receivedCC.getEncapsulatingCC(import_core.CommandClasses["Security 2"], import_cc.Security2Command.MessageEncapsulation); if (!encapCC) return; const isHighestGranted = encapCC.securityClass === self.encodingContext.getHighestSecurityClass(self.id); const cc = import_cc.Security2CC.encapsulate(new import_cc.Security2CCCommandsSupportedReport({ nodeId: controller.ownNodeId, supportedCCs: isHighestGranted ? [...self.implementedCCs.entries()].filter(([ccId, info]) => info.secure && ccId !== import_core.CommandClasses["Security 2"]).map(([ccId]) => ccId) : [] }), self.id, self.securityManagers); return { action: "sendCC", cc }; } } }; const encapsulateS2CC = { async transformIncomingCC(controller, self, receivedCC) { if (receivedCC instanceof import_cc.Security2CCMessageEncapsulation && receivedCC.encapsulated) { receivedCC.encapsulated?.toggleEncapsulationFlag(import_core.EncapsulationFlags.Security, true); return receivedCC.encapsulated; } return receivedCC; }, async transformResponse(controller, self, receivedCC, response) { if (response.action === "sendCC" && receivedCC instanceof import_cc.CommandClass && receivedCC.isEncapsulatedWith(import_core.CommandClasses["Security 2"]) && !(response.cc instanceof import_cc.Security2CCMessageEncapsulation)) { const encapsulated = import_cc.Security2CC.encapsulate(response.cc, self.id, self.securityManagers); response.cc = encapsulated; } return response; } }; const Security2CCHooks = [ encapsulateS2CC ]; const Security2CCBehaviors = [ // Key exchange respondToS2KEXGet, respondToS2KEXSet, handleS2PublicKeyReport, respondToS2KEXReport, handleS2NetworkKeyReport, handleS2TransferEnd, // Normal operation respondToS2NonceGet, handleS2DecodeError, respondToS2CommandsSupportedGet ]; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Security2CCBehaviors, Security2CCHooks }); //# sourceMappingURL=Security2.js.map