zwave-js
Version:
Z-Wave driver written entirely in JavaScript/TypeScript
128 lines • 5.64 kB
JavaScript
import { CommandClass, SecurityCC, SecurityCCCommandEncapsulation, SecurityCCCommandsSupportedGet, SecurityCCCommandsSupportedReport, SecurityCCNetworkKeySet, SecurityCCNetworkKeyVerify, SecurityCCNonceGet, SecurityCCNonceReport, SecurityCCSchemeGet, SecurityCCSchemeReport, } from "@zwave-js/cc";
import { CommandClasses, SecurityManager, isEncapsulationCC, } from "@zwave-js/core";
import { MockZWaveFrameType, createMockZWaveRequestFrame, } from "@zwave-js/testing";
// Respond to Scheme Get with Scheme Report
const respondToSchemeGet = {
handleCC(controller, self, receivedCC) {
if (receivedCC instanceof SecurityCCSchemeGet) {
// This essentially means that the device is going to be bootstrapped
// with Security S0. We need to set up the security manager accordingly
// with the temporary key.
const tempKey = new Uint8Array(16).fill(0x00);
self.securityManagers.securityManager = new SecurityManager({
ownNodeId: self.id,
networkKey: tempKey,
nonceTimeout: 100000,
});
// Now respond to the Scheme Get
const cc = new SecurityCCSchemeReport({
nodeId: controller.ownNodeId,
});
return { action: "sendCC", cc };
}
},
};
// Respond to S0 Nonce Get
const respondToS0NonceGet = {
handleCC(controller, self, receivedCC) {
const sm0Node = self.securityManagers.securityManager;
if (!sm0Node)
return;
if (receivedCC instanceof SecurityCCNonceGet) {
const nonce = sm0Node.generateNonce(controller.ownNodeId, 8);
const cc = new SecurityCCNonceReport({
nodeId: controller.ownNodeId,
nonce,
});
return { action: "sendCC", cc };
}
},
};
// Respond to Network Key Set with Network Key Verify
const respondToNetworkKeySet = {
async handleCC(controller, self, receivedCC) {
if (receivedCC instanceof SecurityCCNetworkKeySet) {
// Update the node's security manager to use the received network key
const receivedKey = receivedCC.networkKey;
self.securityManagers.securityManager.networkKey = receivedKey;
const response = new SecurityCCNetworkKeyVerify({
nodeId: controller.ownNodeId,
});
return { action: "sendCC", cc: response };
}
},
};
// Respond to S0 Commands Supported Get
const respondToS0CommandsSupportedGet = {
async handleCC(controller, self, receivedCC) {
if (receivedCC instanceof SecurityCCCommandsSupportedGet) {
// Determine securely supported CCs from the mock node's implementedCCs
const supportedCCs = [...self.implementedCCs.entries()]
.filter(([cc, info]) => info.secure === true
&& !isEncapsulationCC(cc))
.map(([cc]) => cc);
const response = new SecurityCCCommandsSupportedReport({
nodeId: controller.ownNodeId,
supportedCCs,
controlledCCs: [],
reportsToFollow: 0,
});
return { action: "sendCC", cc: response };
}
},
};
// Parse and unwrap Security CC commands.
const encapsulateS0CC = {
async transformIncomingCC(controller, self, receivedCC) {
// We don't support sequenced commands here
if (receivedCC instanceof SecurityCCCommandEncapsulation) {
await receivedCC.mergePartialCCs([], {
sourceNodeId: controller.ownNodeId,
__internalIsMockNode: true,
frameType: "singlecast",
...self.encodingContext,
...self.securityManagers,
});
// Return the unwrapped command
if (receivedCC.encapsulated) {
return receivedCC.encapsulated;
}
}
return receivedCC;
},
async transformResponse(controller, self, receivedCC, response) {
// Ensure that responses to S0-encapsulated CCs are also S0-encapsulated
if (response.action === "sendCC"
&& receivedCC instanceof CommandClass
&& receivedCC.isEncapsulatedWith(CommandClasses.Security)
&& !response.cc.isEncapsulatedWith(CommandClasses.Security)) {
// The mock node does not have the magic for automatically
// encapsulating commands, so we have to do it ourselves here.
// This requires a nonce exchange.
const nonceGet = new SecurityCCNonceGet({
nodeId: response.cc.nodeId,
});
await self.sendToController(createMockZWaveRequestFrame(nonceGet, {
ackRequested: false,
}));
const nonceReport = await self.expectControllerFrame((resp) => resp.type === MockZWaveFrameType.Request
&& resp.payload instanceof SecurityCCNonceReport, { timeout: 1000 });
const receiverNonce = nonceReport.payload.nonce;
// Encapsulate the response
const encapsulated = SecurityCC.encapsulate(self.id, self.securityManagers.securityManager, response.cc);
encapsulated.nonce = receiverNonce;
response.cc = encapsulated;
}
return response;
},
};
export const SecurityCCHooks = [
encapsulateS0CC,
];
export const SecurityCCBehaviors = [
respondToSchemeGet,
respondToS0NonceGet,
respondToNetworkKeySet,
respondToS0CommandsSupportedGet,
];
//# sourceMappingURL=Security.js.map