zwave-js
Version:
Z-Wave driver written entirely in JavaScript/TypeScript
312 lines (311 loc) • 13.1 kB
JavaScript
"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