inventoresed
Version:
Z-Wave driver written entirely in JavaScript/TypeScript
183 lines (167 loc) • 5.01 kB
text/typescript
import {
BasicCCReport,
BasicCCValues,
CommandClass,
InvalidCC,
Security2CC,
Security2CCNonceGet,
Security2CCNonceReport,
} from "@zwave-js/cc";
import {
SecurityClass,
SecurityManager2,
ZWaveErrorCodes,
} from "@zwave-js/core";
import {
createMockZWaveRequestFrame,
MockZWaveFrameType,
type MockNodeBehavior,
} from "@zwave-js/testing";
import { wait } from "alcalzone-shared/async";
import path from "path";
import { integrationTest } from "../integrationTestSuite";
integrationTest(
"Security S2: Completely discard commands that should have been encrypted, but are not",
{
// debug: true,
// We need the cache to skip the CC interviews and mark S2 as supported
provisioningDirectory: path.join(__dirname, "fixtures/securityS2"),
customSetup: async (driver, controller, mockNode) => {
// Create a security manager for the node
const smNode = new SecurityManager2();
// Copy keys from the driver
smNode.setKey(
SecurityClass.S2_AccessControl,
driver.options.securityKeys!.S2_AccessControl!,
);
smNode.setKey(
SecurityClass.S2_Authenticated,
driver.options.securityKeys!.S2_Authenticated!,
);
smNode.setKey(
SecurityClass.S2_Unauthenticated,
driver.options.securityKeys!.S2_Unauthenticated!,
);
mockNode.host.securityManager2 = smNode;
mockNode.host.getHighestSecurityClass = () =>
SecurityClass.S2_Unauthenticated;
// Create a security manager for the controller
const smCtrlr = new SecurityManager2();
// Copy keys from the driver
smCtrlr.setKey(
SecurityClass.S2_AccessControl,
driver.options.securityKeys!.S2_AccessControl!,
);
smCtrlr.setKey(
SecurityClass.S2_Authenticated,
driver.options.securityKeys!.S2_Authenticated!,
);
smCtrlr.setKey(
SecurityClass.S2_Unauthenticated,
driver.options.securityKeys!.S2_Unauthenticated!,
);
controller.host.securityManager2 = smCtrlr;
controller.host.getHighestSecurityClass = () =>
SecurityClass.S2_Unauthenticated;
// Respond to Nonce Get
const respondToNonceGet: MockNodeBehavior = {
async onControllerFrame(controller, self, frame) {
if (
frame.type === MockZWaveFrameType.Request &&
frame.payload instanceof Security2CCNonceGet
) {
const nonce = smNode.generateNonce(
controller.host.ownNodeId,
);
const cc = new Security2CCNonceReport(self.host, {
nodeId: controller.host.ownNodeId,
SOS: true,
MOS: false,
receiverEI: nonce,
});
await self.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);
return true;
}
return false;
},
};
mockNode.defineBehavior(respondToNonceGet);
// Handle decode errors
const handleInvalidCC: MockNodeBehavior = {
async onControllerFrame(controller, self, frame) {
if (
frame.type === MockZWaveFrameType.Request &&
frame.payload instanceof InvalidCC
) {
if (
frame.payload.reason ===
ZWaveErrorCodes.Security2CC_CannotDecode ||
frame.payload.reason ===
ZWaveErrorCodes.Security2CC_NoSPAN
) {
const nonce = smNode.generateNonce(
controller.host.ownNodeId,
);
const cc = new Security2CCNonceReport(self.host, {
nodeId: controller.host.ownNodeId,
SOS: true,
MOS: false,
receiverEI: nonce,
});
await self.sendToController(
createMockZWaveRequestFrame(cc, {
ackRequested: false,
}),
);
return true;
}
}
return false;
},
};
mockNode.defineBehavior(handleInvalidCC);
},
testBody: async (driver, node, mockController, mockNode) => {
// Send a secure Basic SET to sync the SPAN
await node.commandClasses.Basic.set(1);
driver.driverLog.print("----------");
driver.driverLog.print("START TEST");
driver.driverLog.print("----------");
// Send a secure command that should be handled
let nodeToHost: CommandClass = Security2CC.encapsulate(
mockNode.host,
new BasicCCReport(mockNode.host, {
nodeId: mockController.host.ownNodeId,
currentValue: 99,
}),
);
await mockNode.sendToController(
createMockZWaveRequestFrame(nodeToHost, {
ackRequested: true,
}),
);
// Bit of delay to allow the command to be handled
await wait(100);
let currentValue = node.getValue(BasicCCValues.currentValue.id);
expect(currentValue).toBe(99);
// Then send an unencypted one that should be discarded
nodeToHost = new BasicCCReport(mockNode.host, {
nodeId: mockController.host.ownNodeId,
currentValue: 1,
});
await mockNode.sendToController(
createMockZWaveRequestFrame(nodeToHost, {
ackRequested: true,
}),
);
// Bit of delay to allow the command to be handled
await wait(100);
currentValue = node.getValue(BasicCCValues.currentValue.id);
expect(currentValue).toBe(99); // unchanged
},
},
);