@zwave-js/cc 9.3.1-0-pr-4662-79458fb
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cc/AlarmSensorCC.d.ts +54 -0
- package/build/cc/AlarmSensorCC.d.ts.map +1 -0
- package/build/cc/AlarmSensorCC.js +378 -0
- package/build/cc/AlarmSensorCC.js.map +1 -0
- package/build/cc/AssociationCC.d.ts +123 -0
- package/build/cc/AssociationCC.d.ts.map +1 -0
- package/build/cc/AssociationCC.js +615 -0
- package/build/cc/AssociationCC.js.map +1 -0
- package/build/cc/AssociationGroupInfoCC.d.ts +97 -0
- package/build/cc/AssociationGroupInfoCC.d.ts.map +1 -0
- package/build/cc/AssociationGroupInfoCC.js +530 -0
- package/build/cc/AssociationGroupInfoCC.js.map +1 -0
- package/build/cc/BarrierOperatorCC.d.ts +75 -0
- package/build/cc/BarrierOperatorCC.d.ts.map +1 -0
- package/build/cc/BarrierOperatorCC.js +492 -0
- package/build/cc/BarrierOperatorCC.js.map +1 -0
- package/build/cc/BasicCC.d.ts +51 -0
- package/build/cc/BasicCC.d.ts.map +1 -0
- package/build/cc/BasicCC.js +344 -0
- package/build/cc/BasicCC.js.map +1 -0
- package/build/cc/BatteryCC.d.ts +56 -0
- package/build/cc/BatteryCC.d.ts.map +1 -0
- package/build/cc/BatteryCC.js +417 -0
- package/build/cc/BatteryCC.js.map +1 -0
- package/build/cc/BinarySensorCC.d.ts +52 -0
- package/build/cc/BinarySensorCC.d.ts.map +1 -0
- package/build/cc/BinarySensorCC.js +321 -0
- package/build/cc/BinarySensorCC.js.map +1 -0
- package/build/cc/BinarySwitchCC.d.ts +55 -0
- package/build/cc/BinarySwitchCC.d.ts.map +1 -0
- package/build/cc/BinarySwitchCC.js +327 -0
- package/build/cc/BinarySwitchCC.js.map +1 -0
- package/build/cc/CRC16CC.d.ts +28 -0
- package/build/cc/CRC16CC.d.ts.map +1 -0
- package/build/cc/CRC16CC.js +109 -0
- package/build/cc/CRC16CC.js.map +1 -0
- package/build/cc/CentralSceneCC.d.ts +69 -0
- package/build/cc/CentralSceneCC.d.ts.map +1 -0
- package/build/cc/CentralSceneCC.js +443 -0
- package/build/cc/CentralSceneCC.js.map +1 -0
- package/build/cc/ClimateControlScheduleCC.d.ts +78 -0
- package/build/cc/ClimateControlScheduleCC.d.ts.map +1 -0
- package/build/cc/ClimateControlScheduleCC.js +417 -0
- package/build/cc/ClimateControlScheduleCC.js.map +1 -0
- package/build/cc/ClockCC.d.ts +40 -0
- package/build/cc/ClockCC.d.ts.map +1 -0
- package/build/cc/ClockCC.js +185 -0
- package/build/cc/ClockCC.js.map +1 -0
- package/build/cc/ColorSwitchCC.d.ts +98 -0
- package/build/cc/ColorSwitchCC.d.ts.map +1 -0
- package/build/cc/ColorSwitchCC.js +1045 -0
- package/build/cc/ColorSwitchCC.js.map +1 -0
- package/build/cc/ConfigurationCC.d.ts +315 -0
- package/build/cc/ConfigurationCC.d.ts.map +1 -0
- package/build/cc/ConfigurationCC.js +2257 -0
- package/build/cc/ConfigurationCC.js.map +1 -0
- package/build/cc/DeviceResetLocallyCC.d.ts +9 -0
- package/build/cc/DeviceResetLocallyCC.d.ts.map +1 -0
- package/build/cc/DeviceResetLocallyCC.js +28 -0
- package/build/cc/DeviceResetLocallyCC.js.map +1 -0
- package/build/cc/DoorLockCC.d.ts +116 -0
- package/build/cc/DoorLockCC.d.ts.map +1 -0
- package/build/cc/DoorLockCC.js +1112 -0
- package/build/cc/DoorLockCC.js.map +1 -0
- package/build/cc/DoorLockLoggingCC.d.ts +41 -0
- package/build/cc/DoorLockLoggingCC.d.ts.map +1 -0
- package/build/cc/DoorLockLoggingCC.js +288 -0
- package/build/cc/DoorLockLoggingCC.js.map +1 -0
- package/build/cc/EntryControlCC.d.ts +71 -0
- package/build/cc/EntryControlCC.d.ts.map +1 -0
- package/build/cc/EntryControlCC.js +492 -0
- package/build/cc/EntryControlCC.js.map +1 -0
- package/build/cc/FirmwareUpdateMetaDataCC.d.ts +148 -0
- package/build/cc/FirmwareUpdateMetaDataCC.d.ts.map +1 -0
- package/build/cc/FirmwareUpdateMetaDataCC.js +727 -0
- package/build/cc/FirmwareUpdateMetaDataCC.js.map +1 -0
- package/build/cc/HailCC.d.ts +6 -0
- package/build/cc/HailCC.d.ts.map +1 -0
- package/build/cc/HailCC.js +23 -0
- package/build/cc/HailCC.js.map +1 -0
- package/build/cc/HumidityControlModeCC.d.ts +47 -0
- package/build/cc/HumidityControlModeCC.d.ts.map +1 -0
- package/build/cc/HumidityControlModeCC.js +288 -0
- package/build/cc/HumidityControlModeCC.js.map +1 -0
- package/build/cc/HumidityControlOperatingStateCC.d.ts +24 -0
- package/build/cc/HumidityControlOperatingStateCC.d.ts.map +1 -0
- package/build/cc/HumidityControlOperatingStateCC.js +129 -0
- package/build/cc/HumidityControlOperatingStateCC.js.map +1 -0
- package/build/cc/HumidityControlSetpointCC.d.ts +103 -0
- package/build/cc/HumidityControlSetpointCC.d.ts.map +1 -0
- package/build/cc/HumidityControlSetpointCC.js +626 -0
- package/build/cc/HumidityControlSetpointCC.js.map +1 -0
- package/build/cc/IndicatorCC.d.ts +88 -0
- package/build/cc/IndicatorCC.d.ts.map +1 -0
- package/build/cc/IndicatorCC.js +758 -0
- package/build/cc/IndicatorCC.js.map +1 -0
- package/build/cc/IrrigationCC.d.ts +278 -0
- package/build/cc/IrrigationCC.d.ts.map +1 -0
- package/build/cc/IrrigationCC.js +2062 -0
- package/build/cc/IrrigationCC.js.map +1 -0
- package/build/cc/LanguageCC.d.ts +41 -0
- package/build/cc/LanguageCC.d.ts.map +1 -0
- package/build/cc/LanguageCC.js +220 -0
- package/build/cc/LanguageCC.js.map +1 -0
- package/build/cc/LockCC.d.ts +41 -0
- package/build/cc/LockCC.d.ts.map +1 -0
- package/build/cc/LockCC.js +185 -0
- package/build/cc/LockCC.js.map +1 -0
- package/build/cc/ManufacturerProprietaryCC.d.ts +31 -0
- package/build/cc/ManufacturerProprietaryCC.d.ts.map +1 -0
- package/build/cc/ManufacturerProprietaryCC.js +275 -0
- package/build/cc/ManufacturerProprietaryCC.js.map +1 -0
- package/build/cc/ManufacturerSpecificCC.d.ts +59 -0
- package/build/cc/ManufacturerSpecificCC.d.ts.map +1 -0
- package/build/cc/ManufacturerSpecificCC.js +285 -0
- package/build/cc/ManufacturerSpecificCC.js.map +1 -0
- package/build/cc/MeterCC.d.ts +100 -0
- package/build/cc/MeterCC.d.ts.map +1 -0
- package/build/cc/MeterCC.js +820 -0
- package/build/cc/MeterCC.js.map +1 -0
- package/build/cc/MultiChannelAssociationCC.d.ts +127 -0
- package/build/cc/MultiChannelAssociationCC.d.ts.map +1 -0
- package/build/cc/MultiChannelAssociationCC.js +883 -0
- package/build/cc/MultiChannelAssociationCC.js.map +1 -0
- package/build/cc/MultiChannelCC.d.ts +164 -0
- package/build/cc/MultiChannelCC.d.ts.map +1 -0
- package/build/cc/MultiChannelCC.js +1004 -0
- package/build/cc/MultiChannelCC.js.map +1 -0
- package/build/cc/MultiCommandCC.d.ts +28 -0
- package/build/cc/MultiCommandCC.d.ts.map +1 -0
- package/build/cc/MultiCommandCC.js +137 -0
- package/build/cc/MultiCommandCC.js.map +1 -0
- package/build/cc/MultilevelSensorCC.d.ts +83 -0
- package/build/cc/MultilevelSensorCC.d.ts.map +1 -0
- package/build/cc/MultilevelSensorCC.js +633 -0
- package/build/cc/MultilevelSensorCC.js.map +1 -0
- package/build/cc/MultilevelSwitchCC.d.ts +86 -0
- package/build/cc/MultilevelSwitchCC.d.ts.map +1 -0
- package/build/cc/MultilevelSwitchCC.js +774 -0
- package/build/cc/MultilevelSwitchCC.js.map +1 -0
- package/build/cc/NoOperationCC.d.ts +17 -0
- package/build/cc/NoOperationCC.d.ts.map +1 -0
- package/build/cc/NoOperationCC.js +50 -0
- package/build/cc/NoOperationCC.js.map +1 -0
- package/build/cc/NodeNamingCC.d.ts +57 -0
- package/build/cc/NodeNamingCC.d.ts.map +1 -0
- package/build/cc/NodeNamingCC.js +313 -0
- package/build/cc/NodeNamingCC.js.map +1 -0
- package/build/cc/NotificationCC.d.ts +127 -0
- package/build/cc/NotificationCC.d.ts.map +1 -0
- package/build/cc/NotificationCC.js +1193 -0
- package/build/cc/NotificationCC.js.map +1 -0
- package/build/cc/PowerlevelCC.d.ts +63 -0
- package/build/cc/PowerlevelCC.d.ts.map +1 -0
- package/build/cc/PowerlevelCC.js +262 -0
- package/build/cc/PowerlevelCC.js.map +1 -0
- package/build/cc/ProtectionCC.d.ts +94 -0
- package/build/cc/ProtectionCC.d.ts.map +1 -0
- package/build/cc/ProtectionCC.js +630 -0
- package/build/cc/ProtectionCC.js.map +1 -0
- package/build/cc/SceneActivationCC.d.ts +34 -0
- package/build/cc/SceneActivationCC.d.ts.map +1 -0
- package/build/cc/SceneActivationCC.js +170 -0
- package/build/cc/SceneActivationCC.js.map +1 -0
- package/build/cc/SceneActuatorConfigurationCC.d.ts +52 -0
- package/build/cc/SceneActuatorConfigurationCC.d.ts.map +1 -0
- package/build/cc/SceneActuatorConfigurationCC.js +368 -0
- package/build/cc/SceneActuatorConfigurationCC.js.map +1 -0
- package/build/cc/SceneControllerConfigurationCC.d.ts +61 -0
- package/build/cc/SceneControllerConfigurationCC.d.ts.map +1 -0
- package/build/cc/SceneControllerConfigurationCC.js +444 -0
- package/build/cc/SceneControllerConfigurationCC.js.map +1 -0
- package/build/cc/Security2CC.d.ts +213 -0
- package/build/cc/Security2CC.d.ts.map +1 -0
- package/build/cc/Security2CC.js +1124 -0
- package/build/cc/Security2CC.js.map +1 -0
- package/build/cc/SecurityCC.d.ts +109 -0
- package/build/cc/SecurityCC.d.ts.map +1 -0
- package/build/cc/SecurityCC.js +609 -0
- package/build/cc/SecurityCC.js.map +1 -0
- package/build/cc/SoundSwitchCC.d.ts +87 -0
- package/build/cc/SoundSwitchCC.d.ts.map +1 -0
- package/build/cc/SoundSwitchCC.js +548 -0
- package/build/cc/SoundSwitchCC.js.map +1 -0
- package/build/cc/SupervisionCC.d.ts +63 -0
- package/build/cc/SupervisionCC.d.ts.map +1 -0
- package/build/cc/SupervisionCC.js +239 -0
- package/build/cc/SupervisionCC.js.map +1 -0
- package/build/cc/ThermostatFanModeCC.d.ts +53 -0
- package/build/cc/ThermostatFanModeCC.d.ts.map +1 -0
- package/build/cc/ThermostatFanModeCC.js +359 -0
- package/build/cc/ThermostatFanModeCC.js.map +1 -0
- package/build/cc/ThermostatFanStateCC.d.ts +24 -0
- package/build/cc/ThermostatFanStateCC.d.ts.map +1 -0
- package/build/cc/ThermostatFanStateCC.js +131 -0
- package/build/cc/ThermostatFanStateCC.js.map +1 -0
- package/build/cc/ThermostatModeCC.d.ts +58 -0
- package/build/cc/ThermostatModeCC.d.ts.map +1 -0
- package/build/cc/ThermostatModeCC.js +370 -0
- package/build/cc/ThermostatModeCC.js.map +1 -0
- package/build/cc/ThermostatOperatingStateCC.d.ts +24 -0
- package/build/cc/ThermostatOperatingStateCC.d.ts.map +1 -0
- package/build/cc/ThermostatOperatingStateCC.js +127 -0
- package/build/cc/ThermostatOperatingStateCC.js.map +1 -0
- package/build/cc/ThermostatSetbackCC.d.ts +42 -0
- package/build/cc/ThermostatSetbackCC.d.ts.map +1 -0
- package/build/cc/ThermostatSetbackCC.js +237 -0
- package/build/cc/ThermostatSetbackCC.js.map +1 -0
- package/build/cc/ThermostatSetpointCC.d.ts +98 -0
- package/build/cc/ThermostatSetpointCC.d.ts.map +1 -0
- package/build/cc/ThermostatSetpointCC.js +707 -0
- package/build/cc/ThermostatSetpointCC.js.map +1 -0
- package/build/cc/TimeCC.d.ts +78 -0
- package/build/cc/TimeCC.d.ts.map +1 -0
- package/build/cc/TimeCC.js +373 -0
- package/build/cc/TimeCC.js.map +1 -0
- package/build/cc/TimeParametersCC.d.ts +40 -0
- package/build/cc/TimeParametersCC.d.ts.map +1 -0
- package/build/cc/TimeParametersCC.js +275 -0
- package/build/cc/TimeParametersCC.js.map +1 -0
- package/build/cc/TransportServiceCC.d.ts +101 -0
- package/build/cc/TransportServiceCC.d.ts.map +1 -0
- package/build/cc/TransportServiceCC.js +404 -0
- package/build/cc/TransportServiceCC.js.map +1 -0
- package/build/cc/UserCodeCC.d.ts +219 -0
- package/build/cc/UserCodeCC.d.ts.map +1 -0
- package/build/cc/UserCodeCC.js +1526 -0
- package/build/cc/UserCodeCC.js.map +1 -0
- package/build/cc/VersionCC.d.ts +91 -0
- package/build/cc/VersionCC.d.ts.map +1 -0
- package/build/cc/VersionCC.js +654 -0
- package/build/cc/VersionCC.js.map +1 -0
- package/build/cc/WakeUpCC.d.ts +67 -0
- package/build/cc/WakeUpCC.d.ts.map +1 -0
- package/build/cc/WakeUpCC.js +425 -0
- package/build/cc/WakeUpCC.js.map +1 -0
- package/build/cc/ZWavePlusCC.d.ts +40 -0
- package/build/cc/ZWavePlusCC.d.ts.map +1 -0
- package/build/cc/ZWavePlusCC.js +269 -0
- package/build/cc/ZWavePlusCC.js.map +1 -0
- package/build/cc/index.d.ts +64 -0
- package/build/cc/index.d.ts.map +1 -0
- package/build/cc/index.js +496 -0
- package/build/cc/index.js.map +1 -0
- package/build/cc/manufacturerProprietary/FibaroCC.d.ts +62 -0
- package/build/cc/manufacturerProprietary/FibaroCC.d.ts.map +1 -0
- package/build/cc/manufacturerProprietary/FibaroCC.js +247 -0
- package/build/cc/manufacturerProprietary/FibaroCC.js.map +1 -0
- package/build/index.d.ts +11 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +48 -0
- package/build/index.js.map +1 -0
- package/build/index_safe.d.ts +4 -0
- package/build/index_safe.d.ts.map +1 -0
- package/build/index_safe.js +30 -0
- package/build/index_safe.js.map +1 -0
- package/build/lib/API.d.ts +176 -0
- package/build/lib/API.d.ts.map +1 -0
- package/build/lib/API.js +325 -0
- package/build/lib/API.js.map +1 -0
- package/build/lib/CommandClass.d.ts +341 -0
- package/build/lib/CommandClass.d.ts.map +1 -0
- package/build/lib/CommandClass.js +1088 -0
- package/build/lib/CommandClass.js.map +1 -0
- package/build/lib/EncapsulatingCommandClass.d.ts +35 -0
- package/build/lib/EncapsulatingCommandClass.d.ts.map +1 -0
- package/build/lib/EncapsulatingCommandClass.js +53 -0
- package/build/lib/EncapsulatingCommandClass.js.map +1 -0
- package/build/lib/ICommandClassContainer.d.ts +9 -0
- package/build/lib/ICommandClassContainer.d.ts.map +1 -0
- package/build/lib/ICommandClassContainer.js +12 -0
- package/build/lib/ICommandClassContainer.js.map +1 -0
- package/build/lib/NotificationEventPayload.d.ts +11 -0
- package/build/lib/NotificationEventPayload.d.ts.map +1 -0
- package/build/lib/NotificationEventPayload.js +11 -0
- package/build/lib/NotificationEventPayload.js.map +1 -0
- package/build/lib/Security2/Extension.d.ts +87 -0
- package/build/lib/Security2/Extension.d.ts.map +1 -0
- package/build/lib/Security2/Extension.js +235 -0
- package/build/lib/Security2/Extension.js.map +1 -0
- package/build/lib/Security2/shared.d.ts +31 -0
- package/build/lib/Security2/shared.d.ts.map +1 -0
- package/build/lib/Security2/shared.js +37 -0
- package/build/lib/Security2/shared.js.map +1 -0
- package/build/lib/_Types.d.ts +1446 -0
- package/build/lib/_Types.d.ts.map +1 -0
- package/build/lib/_Types.js +1334 -0
- package/build/lib/_Types.js.map +1 -0
- package/build/lib/serializers.d.ts +24 -0
- package/build/lib/serializers.d.ts.map +1 -0
- package/build/lib/serializers.js +66 -0
- package/build/lib/serializers.js.map +1 -0
- package/build/lib/utils.d.ts +13 -0
- package/build/lib/utils.d.ts.map +1 -0
- package/build/lib/utils.js +675 -0
- package/build/lib/utils.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,1124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var Security2CC_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.Security2CCCommandsSupportedGet = exports.Security2CCCommandsSupportedReport = exports.Security2CCTransferEnd = exports.Security2CCNetworkKeyVerify = exports.Security2CCNetworkKeyGet = exports.Security2CCNetworkKeyReport = exports.Security2CCPublicKeyReport = exports.Security2CCKEXFail = exports.Security2CCKEXSet = exports.Security2CCKEXGet = exports.Security2CCKEXReport = exports.Security2CCNonceGet = exports.Security2CCNonceReport = exports.Security2CCMessageEncapsulation = exports.Security2CC = exports.Security2CCAPI = void 0;
|
|
11
|
+
const core_1 = require("@zwave-js/core");
|
|
12
|
+
const serial_1 = require("@zwave-js/serial");
|
|
13
|
+
const safe_1 = require("@zwave-js/shared/safe");
|
|
14
|
+
const API_1 = require("../lib/API");
|
|
15
|
+
const CommandClass_1 = require("../lib/CommandClass");
|
|
16
|
+
const Extension_1 = require("../lib/Security2/Extension");
|
|
17
|
+
const shared_1 = require("../lib/Security2/shared");
|
|
18
|
+
const _Types_1 = require("../lib/_Types");
|
|
19
|
+
const MultiChannelCC_1 = require("./MultiChannelCC");
|
|
20
|
+
function securityClassToBitMask(key) {
|
|
21
|
+
return (0, core_1.encodeBitMask)([key], core_1.SecurityClass.S0_Legacy, core_1.SecurityClass.S2_Unauthenticated);
|
|
22
|
+
}
|
|
23
|
+
function bitMaskToSecurityClass(buffer, offset) {
|
|
24
|
+
const keys = (0, core_1.parseBitMask)(buffer.slice(offset, offset + 1), core_1.SecurityClass.S2_Unauthenticated);
|
|
25
|
+
(0, core_1.validatePayload)(keys.length === 1);
|
|
26
|
+
return keys[0];
|
|
27
|
+
}
|
|
28
|
+
function getAuthenticationData(sendingNodeId, destination, homeId, commandLength, unencryptedPayload) {
|
|
29
|
+
const ret = Buffer.allocUnsafe(8 + unencryptedPayload.length);
|
|
30
|
+
ret[0] = sendingNodeId;
|
|
31
|
+
ret[1] = destination;
|
|
32
|
+
ret.writeUInt32BE(homeId, 2);
|
|
33
|
+
ret.writeUInt16BE(commandLength, 6);
|
|
34
|
+
// This includes the sequence number and all unencrypted extensions
|
|
35
|
+
unencryptedPayload.copy(ret, 8, 0);
|
|
36
|
+
return ret;
|
|
37
|
+
}
|
|
38
|
+
/** Validates that a sequence number is not a duplicate and updates the SPAN table if it is accepted */
|
|
39
|
+
function validateSequenceNumber(sequenceNumber) {
|
|
40
|
+
const peerNodeID = this.nodeId;
|
|
41
|
+
core_1.validatePayload.withReason("Duplicate command")(!this.host.securityManager2.isDuplicateSinglecast(peerNodeID, sequenceNumber));
|
|
42
|
+
// Not a duplicate, store it
|
|
43
|
+
this.host.securityManager2.storeSequenceNumber(peerNodeID, sequenceNumber);
|
|
44
|
+
}
|
|
45
|
+
function assertSecurity(options) {
|
|
46
|
+
const verb = (0, serial_1.gotDeserializationOptions)(options) ? "decoded" : "sent";
|
|
47
|
+
if (!this.host.ownNodeId) {
|
|
48
|
+
throw new core_1.ZWaveError(`Secure commands (S2) can only be ${verb} when the controller's node id is known!`, core_1.ZWaveErrorCodes.Driver_NotReady);
|
|
49
|
+
}
|
|
50
|
+
else if (!this.host.securityManager2) {
|
|
51
|
+
throw new core_1.ZWaveError(`Secure commands (S2) can only be ${verb} when the network keys for the applHost are set!`, core_1.ZWaveErrorCodes.Driver_NoSecurity);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const DECRYPT_ATTEMPTS = 5;
|
|
55
|
+
// @noValidateArgs - Encapsulation CCs are used internally and too frequently that we
|
|
56
|
+
// want to pay the cost of validating each call
|
|
57
|
+
let Security2CCAPI = class Security2CCAPI extends API_1.CCAPI {
|
|
58
|
+
supportsCommand(_cmd) {
|
|
59
|
+
// All commands are mandatory
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Sends a nonce to the node, either in response to a NonceGet request or a message that failed to decrypt. The message is sent without any retransmission etc.
|
|
64
|
+
* The return value indicates whether a nonce was successfully sent
|
|
65
|
+
*/
|
|
66
|
+
async sendNonce() {
|
|
67
|
+
this.assertSupportsCommand(_Types_1.Security2Command, _Types_1.Security2Command.NonceReport);
|
|
68
|
+
this.assertPhysicalEndpoint(this.endpoint);
|
|
69
|
+
if (!this.applHost.securityManager2) {
|
|
70
|
+
throw new core_1.ZWaveError(`Nonces can only be sent if secure communication is set up!`, core_1.ZWaveErrorCodes.Driver_NoSecurity);
|
|
71
|
+
}
|
|
72
|
+
const receiverEI = this.applHost.securityManager2.generateNonce(this.endpoint.nodeId);
|
|
73
|
+
const cc = new Security2CCNonceReport(this.applHost, {
|
|
74
|
+
nodeId: this.endpoint.nodeId,
|
|
75
|
+
endpoint: this.endpoint.index,
|
|
76
|
+
SOS: true,
|
|
77
|
+
MOS: false,
|
|
78
|
+
receiverEI,
|
|
79
|
+
});
|
|
80
|
+
try {
|
|
81
|
+
await this.applHost.sendCommand(cc, {
|
|
82
|
+
...this.commandOptions,
|
|
83
|
+
// Seems we need these options or some nodes won't accept the nonce
|
|
84
|
+
transmitOptions: core_1.TransmitOptions.ACK | core_1.TransmitOptions.AutoRoute,
|
|
85
|
+
// Only try sending a nonce once
|
|
86
|
+
maxSendAttempts: 1,
|
|
87
|
+
// Nonce requests must be handled immediately
|
|
88
|
+
priority: core_1.MessagePriority.Nonce,
|
|
89
|
+
// We don't want failures causing us to treat the node as asleep or dead
|
|
90
|
+
changeNodeStatusOnMissingACK: false,
|
|
91
|
+
// And we need to react to
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
if ((0, core_1.isTransmissionError)(e)) {
|
|
96
|
+
// The nonce could not be sent, invalidate it
|
|
97
|
+
this.applHost.securityManager2.deleteNonce(this.endpoint.nodeId);
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// Pass other errors through
|
|
102
|
+
throw e;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Queries the securely supported commands for the current security class
|
|
109
|
+
* @param securityClass Can be used to overwrite the security class to use. If this doesn't match the current one, new nonces will need to be exchanged.
|
|
110
|
+
*/
|
|
111
|
+
async getSupportedCommands(securityClass) {
|
|
112
|
+
this.assertSupportsCommand(_Types_1.Security2Command, _Types_1.Security2Command.CommandsSupportedGet);
|
|
113
|
+
let cc = new Security2CCCommandsSupportedGet(this.applHost, {
|
|
114
|
+
nodeId: this.endpoint.nodeId,
|
|
115
|
+
endpoint: this.endpoint.index,
|
|
116
|
+
});
|
|
117
|
+
// Security2CCCommandsSupportedGet is special because we cannot reply on the applHost to do the automatic
|
|
118
|
+
// encapsulation because it would use a different security class. Therefore the entire possible stack
|
|
119
|
+
// of encapsulation needs to be done here
|
|
120
|
+
if (MultiChannelCC_1.MultiChannelCC.requiresEncapsulation(cc)) {
|
|
121
|
+
cc = MultiChannelCC_1.MultiChannelCC.encapsulate(this.applHost, cc);
|
|
122
|
+
}
|
|
123
|
+
cc = Security2CC.encapsulate(this.applHost, cc, securityClass);
|
|
124
|
+
const response = await this.applHost.sendCommand(cc, {
|
|
125
|
+
...this.commandOptions,
|
|
126
|
+
autoEncapsulate: false,
|
|
127
|
+
});
|
|
128
|
+
return response === null || response === void 0 ? void 0 : response.supportedCCs;
|
|
129
|
+
}
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
131
|
+
async getKeyExchangeParameters() {
|
|
132
|
+
this.assertSupportsCommand(_Types_1.Security2Command, _Types_1.Security2Command.KEXGet);
|
|
133
|
+
const cc = new Security2CCKEXGet(this.applHost, {
|
|
134
|
+
nodeId: this.endpoint.nodeId,
|
|
135
|
+
endpoint: this.endpoint.index,
|
|
136
|
+
});
|
|
137
|
+
const response = await this.applHost.sendCommand(cc, this.commandOptions);
|
|
138
|
+
if (response) {
|
|
139
|
+
return (0, safe_1.pick)(response, [
|
|
140
|
+
"requestCSA",
|
|
141
|
+
"echo",
|
|
142
|
+
"supportedKEXSchemes",
|
|
143
|
+
"supportedECDHProfiles",
|
|
144
|
+
"requestedKeys",
|
|
145
|
+
]);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** Grants the joining node the given keys */
|
|
149
|
+
async grantKeys(params) {
|
|
150
|
+
this.assertSupportsCommand(_Types_1.Security2Command, _Types_1.Security2Command.KEXSet);
|
|
151
|
+
const cc = new Security2CCKEXSet(this.applHost, {
|
|
152
|
+
nodeId: this.endpoint.nodeId,
|
|
153
|
+
endpoint: this.endpoint.index,
|
|
154
|
+
...params,
|
|
155
|
+
echo: false,
|
|
156
|
+
});
|
|
157
|
+
await this.applHost.sendCommand(cc, this.commandOptions);
|
|
158
|
+
}
|
|
159
|
+
/** Confirms the keys that were granted to a node */
|
|
160
|
+
async confirmGrantedKeys(params) {
|
|
161
|
+
this.assertSupportsCommand(_Types_1.Security2Command, _Types_1.Security2Command.KEXReport);
|
|
162
|
+
const cc = new Security2CCKEXReport(this.applHost, {
|
|
163
|
+
nodeId: this.endpoint.nodeId,
|
|
164
|
+
endpoint: this.endpoint.index,
|
|
165
|
+
...params,
|
|
166
|
+
echo: true,
|
|
167
|
+
});
|
|
168
|
+
await this.applHost.sendCommand(cc, this.commandOptions);
|
|
169
|
+
}
|
|
170
|
+
/** Notifies the other node that the ongoing key exchange was aborted */
|
|
171
|
+
async abortKeyExchange(failType) {
|
|
172
|
+
this.assertSupportsCommand(_Types_1.Security2Command, _Types_1.Security2Command.KEXFail);
|
|
173
|
+
const cc = new Security2CCKEXFail(this.applHost, {
|
|
174
|
+
nodeId: this.endpoint.nodeId,
|
|
175
|
+
endpoint: this.endpoint.index,
|
|
176
|
+
failType,
|
|
177
|
+
});
|
|
178
|
+
await this.applHost.sendCommand(cc, this.commandOptions);
|
|
179
|
+
}
|
|
180
|
+
async sendPublicKey(publicKey) {
|
|
181
|
+
this.assertSupportsCommand(_Types_1.Security2Command, _Types_1.Security2Command.PublicKeyReport);
|
|
182
|
+
const cc = new Security2CCPublicKeyReport(this.applHost, {
|
|
183
|
+
nodeId: this.endpoint.nodeId,
|
|
184
|
+
endpoint: this.endpoint.index,
|
|
185
|
+
includingNode: true,
|
|
186
|
+
publicKey,
|
|
187
|
+
});
|
|
188
|
+
await this.applHost.sendCommand(cc, this.commandOptions);
|
|
189
|
+
}
|
|
190
|
+
async sendNetworkKey(securityClass, networkKey) {
|
|
191
|
+
this.assertSupportsCommand(_Types_1.Security2Command, _Types_1.Security2Command.NetworkKeyReport);
|
|
192
|
+
const cc = new Security2CCNetworkKeyReport(this.applHost, {
|
|
193
|
+
nodeId: this.endpoint.nodeId,
|
|
194
|
+
endpoint: this.endpoint.index,
|
|
195
|
+
grantedKey: securityClass,
|
|
196
|
+
networkKey,
|
|
197
|
+
});
|
|
198
|
+
await this.applHost.sendCommand(cc, this.commandOptions);
|
|
199
|
+
}
|
|
200
|
+
async confirmKeyVerification() {
|
|
201
|
+
this.assertSupportsCommand(_Types_1.Security2Command, _Types_1.Security2Command.TransferEnd);
|
|
202
|
+
const cc = new Security2CCTransferEnd(this.applHost, {
|
|
203
|
+
nodeId: this.endpoint.nodeId,
|
|
204
|
+
endpoint: this.endpoint.index,
|
|
205
|
+
keyVerified: true,
|
|
206
|
+
keyRequestComplete: false,
|
|
207
|
+
});
|
|
208
|
+
await this.applHost.sendCommand(cc, {
|
|
209
|
+
...this.commandOptions,
|
|
210
|
+
// Don't wait for an ACK from the node
|
|
211
|
+
transmitOptions: core_1.TransmitOptions.DEFAULT & ~core_1.TransmitOptions.ACK,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
Security2CCAPI = __decorate([
|
|
216
|
+
(0, CommandClass_1.API)(core_1.CommandClasses["Security 2"])
|
|
217
|
+
], Security2CCAPI);
|
|
218
|
+
exports.Security2CCAPI = Security2CCAPI;
|
|
219
|
+
let Security2CC = Security2CC_1 = class Security2CC extends CommandClass_1.CommandClass {
|
|
220
|
+
async interview(applHost) {
|
|
221
|
+
var _a;
|
|
222
|
+
const node = this.getNode(applHost);
|
|
223
|
+
const endpoint = this.getEndpoint(applHost);
|
|
224
|
+
const api = API_1.CCAPI.create(core_1.CommandClasses["Security 2"], applHost, endpoint).withOptions({
|
|
225
|
+
priority: core_1.MessagePriority.NodeQuery,
|
|
226
|
+
});
|
|
227
|
+
// Only on the highest security class the reponse includes the supported commands
|
|
228
|
+
const secClass = node.getHighestSecurityClass();
|
|
229
|
+
let hasReceivedSecureCommands = false;
|
|
230
|
+
let possibleSecurityClasses;
|
|
231
|
+
if ((0, core_1.securityClassIsS2)(secClass)) {
|
|
232
|
+
// The highest security class is known to be S2, only query that one
|
|
233
|
+
possibleSecurityClasses = [secClass];
|
|
234
|
+
}
|
|
235
|
+
else if (endpoint.index === 0) {
|
|
236
|
+
// If the highest security class isn't known, query all possible security classes
|
|
237
|
+
// but only on the root device
|
|
238
|
+
possibleSecurityClasses = [
|
|
239
|
+
core_1.SecurityClass.S2_Unauthenticated,
|
|
240
|
+
core_1.SecurityClass.S2_Authenticated,
|
|
241
|
+
core_1.SecurityClass.S2_AccessControl,
|
|
242
|
+
];
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// For endpoint interviews, the security class MUST be known
|
|
246
|
+
applHost.controllerLog.logNode(node.id, {
|
|
247
|
+
endpoint: endpoint.index,
|
|
248
|
+
message: `Cannot query securely supported commands for endpoint because the node's security class isn't known...`,
|
|
249
|
+
level: "error",
|
|
250
|
+
});
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
for (const secClass of possibleSecurityClasses) {
|
|
254
|
+
// We might not know all assigned security classes yet, so we work our way up from low to high and try to request the supported commands.
|
|
255
|
+
// This way, each command is encrypted with the security class we're currently testing.
|
|
256
|
+
// If the node does not respond, it wasn't assigned the security class.
|
|
257
|
+
// If it responds with a non-empty list, we know this is the highest class it supports.
|
|
258
|
+
// If the list is empty, the security class is still supported.
|
|
259
|
+
// If we already know the class is not supported, skip it
|
|
260
|
+
if (node.hasSecurityClass(secClass) === false)
|
|
261
|
+
continue;
|
|
262
|
+
// If no key is configured for this security class, skip it
|
|
263
|
+
if (!((_a = this.host.securityManager2) === null || _a === void 0 ? void 0 : _a.hasKeysForSecurityClass(secClass))) {
|
|
264
|
+
applHost.controllerLog.logNode(node.id, {
|
|
265
|
+
endpoint: endpoint.index,
|
|
266
|
+
message: `Cannot query securely supported commands (${(0, safe_1.getEnumMemberName)(core_1.SecurityClass, secClass)}) - network key is not configured...`,
|
|
267
|
+
level: "warn",
|
|
268
|
+
});
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
applHost.controllerLog.logNode(node.id, {
|
|
272
|
+
endpoint: endpoint.index,
|
|
273
|
+
message: `Querying securely supported commands (${(0, safe_1.getEnumMemberName)(core_1.SecurityClass, secClass)})...`,
|
|
274
|
+
direction: "outbound",
|
|
275
|
+
});
|
|
276
|
+
// Query the supported commands but avoid remembering the wrong security class in case of a failure
|
|
277
|
+
let supportedCCs;
|
|
278
|
+
try {
|
|
279
|
+
supportedCCs = await api.getSupportedCommands(secClass);
|
|
280
|
+
}
|
|
281
|
+
catch (e) {
|
|
282
|
+
if ((0, core_1.isZWaveError)(e) &&
|
|
283
|
+
e.code === core_1.ZWaveErrorCodes.Security2CC_CannotDecode) {
|
|
284
|
+
// This has to be expected when we're using a non-granted security class
|
|
285
|
+
supportedCCs = undefined;
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
throw e;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (supportedCCs == undefined) {
|
|
292
|
+
if (endpoint.index === 0) {
|
|
293
|
+
// No supported commands found, mark the security class as not granted
|
|
294
|
+
node.setSecurityClass(secClass, false);
|
|
295
|
+
applHost.controllerLog.logNode(node.id, {
|
|
296
|
+
message: `The node was NOT granted the security class ${(0, safe_1.getEnumMemberName)(core_1.SecurityClass, secClass)}`,
|
|
297
|
+
direction: "inbound",
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if (endpoint.index === 0) {
|
|
303
|
+
// Mark the security class as granted
|
|
304
|
+
node.setSecurityClass(secClass, true);
|
|
305
|
+
applHost.controllerLog.logNode(node.id, {
|
|
306
|
+
message: `The node was granted the security class ${(0, safe_1.getEnumMemberName)(core_1.SecurityClass, secClass)}`,
|
|
307
|
+
direction: "inbound",
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
if (!hasReceivedSecureCommands && supportedCCs.length > 0) {
|
|
311
|
+
hasReceivedSecureCommands = true;
|
|
312
|
+
const logLines = [
|
|
313
|
+
`received secure commands (${(0, safe_1.getEnumMemberName)(core_1.SecurityClass, secClass)})`,
|
|
314
|
+
"supported CCs:",
|
|
315
|
+
];
|
|
316
|
+
for (const cc of supportedCCs) {
|
|
317
|
+
logLines.push(`· ${(0, core_1.getCCName)(cc)}`);
|
|
318
|
+
}
|
|
319
|
+
applHost.controllerLog.logNode(node.id, {
|
|
320
|
+
endpoint: endpoint.index,
|
|
321
|
+
message: logLines.join("\n"),
|
|
322
|
+
direction: "inbound",
|
|
323
|
+
});
|
|
324
|
+
// Remember which commands are supported securely
|
|
325
|
+
for (const cc of supportedCCs) {
|
|
326
|
+
endpoint.addCC(cc, {
|
|
327
|
+
isSupported: true,
|
|
328
|
+
secure: true,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Remember that the interview is complete
|
|
334
|
+
this.setInterviewComplete(applHost, true);
|
|
335
|
+
}
|
|
336
|
+
/** Tests if a command should be sent secure and thus requires encapsulation */
|
|
337
|
+
static requiresEncapsulation(cc) {
|
|
338
|
+
// Everything that's not an S2 CC needs to be encapsulated if the CC is secure
|
|
339
|
+
if (!cc.secure)
|
|
340
|
+
return false;
|
|
341
|
+
if (!(cc instanceof Security2CC_1))
|
|
342
|
+
return true;
|
|
343
|
+
// These S2 commands need additional encapsulation
|
|
344
|
+
switch (cc.ccCommand) {
|
|
345
|
+
case _Types_1.Security2Command.CommandsSupportedGet:
|
|
346
|
+
case _Types_1.Security2Command.CommandsSupportedReport:
|
|
347
|
+
case _Types_1.Security2Command.NetworkKeyGet:
|
|
348
|
+
case _Types_1.Security2Command.NetworkKeyReport:
|
|
349
|
+
case _Types_1.Security2Command.NetworkKeyVerify:
|
|
350
|
+
case _Types_1.Security2Command.TransferEnd:
|
|
351
|
+
return true;
|
|
352
|
+
case _Types_1.Security2Command.KEXSet:
|
|
353
|
+
case _Types_1.Security2Command.KEXReport:
|
|
354
|
+
// KEXSet/Report need to be encrypted for the confirmation only
|
|
355
|
+
return cc.echo;
|
|
356
|
+
case _Types_1.Security2Command.KEXFail: {
|
|
357
|
+
switch (cc.failType) {
|
|
358
|
+
case shared_1.KEXFailType.Decrypt:
|
|
359
|
+
case shared_1.KEXFailType.WrongSecurityLevel:
|
|
360
|
+
case shared_1.KEXFailType.KeyNotGranted:
|
|
361
|
+
case shared_1.KEXFailType.NoVerify:
|
|
362
|
+
return true;
|
|
363
|
+
default:
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
/** Encapsulates a command that should be sent encrypted */
|
|
371
|
+
static encapsulate(host, cc, securityClass) {
|
|
372
|
+
return new Security2CCMessageEncapsulation(host, {
|
|
373
|
+
nodeId: cc.nodeId,
|
|
374
|
+
encapsulated: cc,
|
|
375
|
+
securityClass,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
Security2CC = Security2CC_1 = __decorate([
|
|
380
|
+
(0, CommandClass_1.commandClass)(core_1.CommandClasses["Security 2"]),
|
|
381
|
+
(0, CommandClass_1.implementedVersion)(1)
|
|
382
|
+
], Security2CC);
|
|
383
|
+
exports.Security2CC = Security2CC;
|
|
384
|
+
// An S2 encapsulated command may result in a NonceReport to be sent by the node if it couldn't decrypt the message
|
|
385
|
+
function getCCResponseForMessageEncapsulation(sent) {
|
|
386
|
+
var _a;
|
|
387
|
+
if ((_a = sent.encapsulated) === null || _a === void 0 ? void 0 : _a.expectsCCResponse()) {
|
|
388
|
+
return [
|
|
389
|
+
Security2CCMessageEncapsulation,
|
|
390
|
+
Security2CCNonceReport,
|
|
391
|
+
];
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function testCCResponseForMessageEncapsulation(sent, received) {
|
|
395
|
+
if (received instanceof Security2CCMessageEncapsulation) {
|
|
396
|
+
return "checkEncapsulated";
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
return received.SOS && !!received.receiverEI;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
let Security2CCMessageEncapsulation = class Security2CCMessageEncapsulation extends Security2CC {
|
|
403
|
+
constructor(host, options) {
|
|
404
|
+
var _a;
|
|
405
|
+
super(host, options);
|
|
406
|
+
// Make sure that we can send/receive secure commands
|
|
407
|
+
assertSecurity.call(this, options);
|
|
408
|
+
if ((0, serial_1.gotDeserializationOptions)(options)) {
|
|
409
|
+
(0, core_1.validatePayload)(this.payload.length >= 2);
|
|
410
|
+
// Check the sequence number to avoid duplicates
|
|
411
|
+
// TODO: distinguish between multicast and singlecast
|
|
412
|
+
this._sequenceNumber = this.payload[0];
|
|
413
|
+
// Don't accept duplicate commands
|
|
414
|
+
validateSequenceNumber.call(this, this._sequenceNumber);
|
|
415
|
+
// Ensure the node has a security class
|
|
416
|
+
const peerNodeID = this.nodeId;
|
|
417
|
+
// const node = this.getNode()!;
|
|
418
|
+
// validatePayload.withReason("The node is not included")(!!node);
|
|
419
|
+
const securityClass = this.host.getHighestSecurityClass(peerNodeID);
|
|
420
|
+
core_1.validatePayload.withReason("No security class granted")(securityClass !== core_1.SecurityClass.None);
|
|
421
|
+
const hasExtensions = !!(this.payload[1] & 0b1);
|
|
422
|
+
const hasEncryptedExtensions = !!(this.payload[1] & 0b10);
|
|
423
|
+
let offset = 2;
|
|
424
|
+
this.extensions = [];
|
|
425
|
+
const parseExtensions = (buffer) => {
|
|
426
|
+
while (true) {
|
|
427
|
+
// we need to read at least the length byte
|
|
428
|
+
(0, core_1.validatePayload)(buffer.length >= offset + 1);
|
|
429
|
+
const extensionLength = Extension_1.Security2Extension.getExtensionLength(buffer.slice(offset));
|
|
430
|
+
// Parse the extension
|
|
431
|
+
const ext = Extension_1.Security2Extension.from(buffer.slice(offset, offset + extensionLength));
|
|
432
|
+
this.extensions.push(ext);
|
|
433
|
+
offset += extensionLength;
|
|
434
|
+
// Check if that was the last extension
|
|
435
|
+
if (!ext.moreToFollow)
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
if (hasExtensions)
|
|
440
|
+
parseExtensions(this.payload);
|
|
441
|
+
const unencryptedPayload = this.payload.slice(0, offset);
|
|
442
|
+
const ciphertext = this.payload.slice(offset, -core_1.SECURITY_S2_AUTH_TAG_LENGTH);
|
|
443
|
+
const authTag = this.payload.slice(-core_1.SECURITY_S2_AUTH_TAG_LENGTH);
|
|
444
|
+
const messageLength = super.computeEncapsulationOverhead() + this.payload.length;
|
|
445
|
+
const authData = getAuthenticationData(this.nodeId, this.getDestinationIDRX(), this.host.homeId, messageLength, unencryptedPayload);
|
|
446
|
+
// Decrypt payload and verify integrity
|
|
447
|
+
const spanState = this.host.securityManager2.getSPANState(peerNodeID);
|
|
448
|
+
const failNoSPAN = () => {
|
|
449
|
+
return core_1.validatePayload.fail(core_1.ZWaveErrorCodes.Security2CC_NoSPAN);
|
|
450
|
+
};
|
|
451
|
+
// If we are not able to establish an SPAN yet, fail the decryption
|
|
452
|
+
if (spanState.type === core_1.SPANState.None) {
|
|
453
|
+
return failNoSPAN();
|
|
454
|
+
}
|
|
455
|
+
else if (spanState.type === core_1.SPANState.RemoteEI) {
|
|
456
|
+
// TODO: The specs are not clear how to handle this case
|
|
457
|
+
// For now, do the same as if we didn't have any EI
|
|
458
|
+
return failNoSPAN();
|
|
459
|
+
}
|
|
460
|
+
const decrypt = () => {
|
|
461
|
+
const getNonceAndDecrypt = () => {
|
|
462
|
+
const iv = this.host.securityManager2.nextNonce(peerNodeID);
|
|
463
|
+
const { keyCCM: key } = this.host.securityManager2.getKeysForNode(peerNodeID);
|
|
464
|
+
return {
|
|
465
|
+
decryptionKey: key,
|
|
466
|
+
...(0, core_1.decryptAES128CCM)(key, iv, ciphertext, authData, authTag),
|
|
467
|
+
};
|
|
468
|
+
};
|
|
469
|
+
if (spanState.type === core_1.SPANState.SPAN) {
|
|
470
|
+
// This can only happen if the security class is known
|
|
471
|
+
return getNonceAndDecrypt();
|
|
472
|
+
}
|
|
473
|
+
else if (spanState.type === core_1.SPANState.LocalEI) {
|
|
474
|
+
// We've sent the other our receiver's EI and received its sender's EI,
|
|
475
|
+
// meaning we can now establish an SPAN
|
|
476
|
+
const senderEI = this.getSenderEI();
|
|
477
|
+
if (!senderEI)
|
|
478
|
+
return failNoSPAN();
|
|
479
|
+
const receiverEI = spanState.receiverEI;
|
|
480
|
+
// How we do this depends on whether we know the security class of the other node
|
|
481
|
+
if (this.host.securityManager2.tempKeys.has(peerNodeID)) {
|
|
482
|
+
// We're currently bootstrapping the node, it might be using a temporary key
|
|
483
|
+
this.host.securityManager2.initializeTempSPAN(peerNodeID, senderEI, receiverEI);
|
|
484
|
+
const ret = getNonceAndDecrypt();
|
|
485
|
+
// Decryption with the temporary key worked
|
|
486
|
+
if (ret.authOK)
|
|
487
|
+
return ret;
|
|
488
|
+
// Reset the SPAN state and try with the recently granted security class
|
|
489
|
+
this.host.securityManager2.setSPANState(peerNodeID, spanState);
|
|
490
|
+
}
|
|
491
|
+
if (securityClass != undefined) {
|
|
492
|
+
this.host.securityManager2.initializeSPAN(peerNodeID, securityClass, senderEI, receiverEI);
|
|
493
|
+
return getNonceAndDecrypt();
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
// Not knowing it can happen if we just took over an existing network
|
|
497
|
+
// Try multiple security classes
|
|
498
|
+
const possibleSecurityClasses = core_1.securityClassOrder
|
|
499
|
+
.filter((s) => (0, core_1.securityClassIsS2)(s))
|
|
500
|
+
.filter((s) => this.host.hasSecurityClass(peerNodeID, s) !== false);
|
|
501
|
+
for (const secClass of possibleSecurityClasses) {
|
|
502
|
+
// Initialize an SPAN with that security class
|
|
503
|
+
this.host.securityManager2.initializeSPAN(peerNodeID, secClass, senderEI, receiverEI);
|
|
504
|
+
const ret = getNonceAndDecrypt();
|
|
505
|
+
// It worked, return the result and remember the security class
|
|
506
|
+
if (ret.authOK) {
|
|
507
|
+
this.host.setSecurityClass(peerNodeID, secClass, true);
|
|
508
|
+
return ret;
|
|
509
|
+
}
|
|
510
|
+
// Reset the SPAN state and try with the next security class
|
|
511
|
+
this.host.securityManager2.setSPANState(peerNodeID, spanState);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
// Nothing worked, fail the decryption
|
|
516
|
+
return { plaintext: Buffer.from([]), authOK: false };
|
|
517
|
+
};
|
|
518
|
+
let plaintext;
|
|
519
|
+
let authOK = false;
|
|
520
|
+
let decryptionKey;
|
|
521
|
+
// If the Receiver is unable to authenticate the singlecast message with the current SPAN,
|
|
522
|
+
// the Receiver SHOULD try decrypting the message with one or more of the following SPAN values,
|
|
523
|
+
// stopping when decryption is successful or the maximum number of iterations is reached.
|
|
524
|
+
for (let i = 0; i < DECRYPT_ATTEMPTS; i++) {
|
|
525
|
+
({ plaintext, authOK, decryptionKey } = decrypt());
|
|
526
|
+
if (!!authOK && !!plaintext)
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
// If authentication fails, do so with an error code that instructs the
|
|
530
|
+
// applHost to tell the node we have no nonce
|
|
531
|
+
if (!authOK || !plaintext) {
|
|
532
|
+
return core_1.validatePayload.fail(core_1.ZWaveErrorCodes.Security2CC_CannotDecode);
|
|
533
|
+
}
|
|
534
|
+
offset = 0;
|
|
535
|
+
if (hasEncryptedExtensions)
|
|
536
|
+
parseExtensions(plaintext);
|
|
537
|
+
// Not every S2 message includes an encapsulated CC
|
|
538
|
+
const decryptedCCBytes = plaintext.slice(offset);
|
|
539
|
+
if (decryptedCCBytes.length > 0) {
|
|
540
|
+
// make sure this contains a complete CC command that's worth splitting
|
|
541
|
+
(0, core_1.validatePayload)(decryptedCCBytes.length >= 2);
|
|
542
|
+
// and deserialize the CC
|
|
543
|
+
this.encapsulated = CommandClass_1.CommandClass.from(this.host, {
|
|
544
|
+
data: decryptedCCBytes,
|
|
545
|
+
fromEncapsulation: true,
|
|
546
|
+
encapCC: this,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
this.decryptionKey = decryptionKey;
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
this._securityClass = options.securityClass;
|
|
553
|
+
this.encapsulated = options.encapsulated;
|
|
554
|
+
options.encapsulated.encapsulatingCC = this;
|
|
555
|
+
this.extensions = (_a = options.extensions) !== null && _a !== void 0 ? _a : [];
|
|
556
|
+
if (typeof this.nodeId !== "number" &&
|
|
557
|
+
!this.extensions.some((e) => e instanceof Extension_1.MGRPExtension)) {
|
|
558
|
+
throw new core_1.ZWaveError("Multicast Security S2 encapsulation requires the MGRP extension", core_1.ZWaveErrorCodes.Security2CC_MissingExtension);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Return the sequence number of this command.
|
|
564
|
+
*
|
|
565
|
+
* **WARNING:** If the sequence number hasn't been set before, this will create a new one.
|
|
566
|
+
* When sending messages, this should only happen immediately before serializing.
|
|
567
|
+
*/
|
|
568
|
+
get sequenceNumber() {
|
|
569
|
+
if (this._sequenceNumber == undefined) {
|
|
570
|
+
this._sequenceNumber =
|
|
571
|
+
this.host.securityManager2.nextSequenceNumber(this.nodeId);
|
|
572
|
+
}
|
|
573
|
+
return this._sequenceNumber;
|
|
574
|
+
}
|
|
575
|
+
unsetSequenceNumber() {
|
|
576
|
+
this._sequenceNumber = undefined;
|
|
577
|
+
}
|
|
578
|
+
getDestinationIDTX() {
|
|
579
|
+
const mgrpExtension = this.extensions.find((e) => e instanceof Extension_1.MGRPExtension);
|
|
580
|
+
if (mgrpExtension)
|
|
581
|
+
return mgrpExtension.groupId;
|
|
582
|
+
else if (typeof this.nodeId === "number")
|
|
583
|
+
return this.nodeId;
|
|
584
|
+
throw new core_1.ZWaveError("Multicast Security S2 encapsulation requires the MGRP extension", core_1.ZWaveErrorCodes.Security2CC_MissingExtension);
|
|
585
|
+
}
|
|
586
|
+
getDestinationIDRX() {
|
|
587
|
+
const mgrpExtension = this.extensions.find((e) => e instanceof Extension_1.MGRPExtension);
|
|
588
|
+
if (mgrpExtension)
|
|
589
|
+
return mgrpExtension.groupId;
|
|
590
|
+
return this.host.ownNodeId;
|
|
591
|
+
}
|
|
592
|
+
/** Returns the Sender's Entropy Input if this command contains an SPAN extension */
|
|
593
|
+
getSenderEI() {
|
|
594
|
+
const spanExtension = this.extensions.find((e) => e instanceof Extension_1.SPANExtension);
|
|
595
|
+
return spanExtension === null || spanExtension === void 0 ? void 0 : spanExtension.senderEI;
|
|
596
|
+
}
|
|
597
|
+
serialize() {
|
|
598
|
+
var _a, _b, _c;
|
|
599
|
+
// TODO: Support Multicast
|
|
600
|
+
const nodeId = this.nodeId;
|
|
601
|
+
// Include Sender EI in the command if we only have the receiver's EI
|
|
602
|
+
const spanState = this.host.securityManager2.getSPANState(nodeId);
|
|
603
|
+
if (spanState.type === core_1.SPANState.None ||
|
|
604
|
+
spanState.type === core_1.SPANState.LocalEI) {
|
|
605
|
+
// Can't do anything here if we don't have the receiver's EI
|
|
606
|
+
throw new core_1.ZWaveError(`Security S2 CC requires the receiver's nonce to be sent!`, core_1.ZWaveErrorCodes.Security2CC_NoSPAN);
|
|
607
|
+
}
|
|
608
|
+
else if (spanState.type === core_1.SPANState.RemoteEI) {
|
|
609
|
+
// We have the receiver's EI, generate our input and send it over
|
|
610
|
+
// With both, we can create an SPAN
|
|
611
|
+
const senderEI = this.host.securityManager2.generateNonce(undefined);
|
|
612
|
+
const receiverEI = spanState.receiverEI;
|
|
613
|
+
// While bootstrapping a node, the controller only sends commands encrypted
|
|
614
|
+
// with the temporary key
|
|
615
|
+
if (this.host.securityManager2.tempKeys.has(nodeId)) {
|
|
616
|
+
this.host.securityManager2.initializeTempSPAN(nodeId, senderEI, receiverEI);
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
const securityClass = (_a = this._securityClass) !== null && _a !== void 0 ? _a : this.host.getHighestSecurityClass(nodeId);
|
|
620
|
+
if (securityClass == undefined) {
|
|
621
|
+
throw new core_1.ZWaveError("No security class defined for this command!", core_1.ZWaveErrorCodes.Security2CC_NoSPAN);
|
|
622
|
+
}
|
|
623
|
+
this.host.securityManager2.initializeSPAN(nodeId, securityClass, senderEI, receiverEI);
|
|
624
|
+
}
|
|
625
|
+
// Add or update the SPAN extension
|
|
626
|
+
let spanExtension = this.extensions.find((e) => e instanceof Extension_1.SPANExtension);
|
|
627
|
+
if (spanExtension) {
|
|
628
|
+
spanExtension.senderEI = senderEI;
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
spanExtension = new Extension_1.SPANExtension({ senderEI });
|
|
632
|
+
this.extensions.push(spanExtension);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
const unencryptedExtensions = this.extensions.filter((e) => !e.isEncrypted());
|
|
636
|
+
const encryptedExtensions = this.extensions.filter((e) => e.isEncrypted());
|
|
637
|
+
const unencryptedPayload = Buffer.concat([
|
|
638
|
+
Buffer.from([
|
|
639
|
+
this.sequenceNumber,
|
|
640
|
+
(encryptedExtensions.length > 0 ? 0b10 : 0) |
|
|
641
|
+
(unencryptedExtensions.length > 0 ? 1 : 0),
|
|
642
|
+
]),
|
|
643
|
+
...unencryptedExtensions.map((e, index) => e.serialize(index < unencryptedExtensions.length - 1)),
|
|
644
|
+
]);
|
|
645
|
+
const serializedCC = (_c = (_b = this.encapsulated) === null || _b === void 0 ? void 0 : _b.serialize()) !== null && _c !== void 0 ? _c : Buffer.from([]);
|
|
646
|
+
const plaintextPayload = Buffer.concat([
|
|
647
|
+
...encryptedExtensions.map((e, index) => e.serialize(index < encryptedExtensions.length - 1)),
|
|
648
|
+
serializedCC,
|
|
649
|
+
]);
|
|
650
|
+
// Generate the authentication data for CCM encryption
|
|
651
|
+
const messageLength = this.computeEncapsulationOverhead() + serializedCC.length;
|
|
652
|
+
const authData = getAuthenticationData(this.host.ownNodeId, this.getDestinationIDTX(), this.host.homeId, messageLength, unencryptedPayload);
|
|
653
|
+
const iv = this.host.securityManager2.nextNonce(nodeId);
|
|
654
|
+
const { keyCCM: key } =
|
|
655
|
+
// Prefer the overridden security class if it was given
|
|
656
|
+
this._securityClass != undefined
|
|
657
|
+
? this.host.securityManager2.getKeysForSecurityClass(this._securityClass)
|
|
658
|
+
: this.host.securityManager2.getKeysForNode(nodeId);
|
|
659
|
+
const { ciphertext: ciphertextPayload, authTag } = (0, core_1.encryptAES128CCM)(key, iv, plaintextPayload, authData, core_1.SECURITY_S2_AUTH_TAG_LENGTH);
|
|
660
|
+
this.payload = Buffer.concat([
|
|
661
|
+
unencryptedPayload,
|
|
662
|
+
ciphertextPayload,
|
|
663
|
+
authTag,
|
|
664
|
+
]);
|
|
665
|
+
return super.serialize();
|
|
666
|
+
}
|
|
667
|
+
computeEncapsulationOverhead() {
|
|
668
|
+
// Security S2 adds:
|
|
669
|
+
// * 1 byte sequence number
|
|
670
|
+
// * 1 byte control
|
|
671
|
+
// * N bytes extensions
|
|
672
|
+
// * SECURITY_S2_AUTH_TAG_LENGTH bytes auth tag
|
|
673
|
+
const extensionBytes = this.extensions
|
|
674
|
+
.map((e) => e.computeLength())
|
|
675
|
+
.reduce((a, b) => a + b, 0);
|
|
676
|
+
return (super.computeEncapsulationOverhead() +
|
|
677
|
+
2 +
|
|
678
|
+
core_1.SECURITY_S2_AUTH_TAG_LENGTH +
|
|
679
|
+
extensionBytes);
|
|
680
|
+
}
|
|
681
|
+
toLogEntry(applHost) {
|
|
682
|
+
const message = {
|
|
683
|
+
"sequence number": this.sequenceNumber,
|
|
684
|
+
};
|
|
685
|
+
if (this.extensions.length > 0) {
|
|
686
|
+
message.extensions = this.extensions
|
|
687
|
+
.map((e) => e.toLogEntry())
|
|
688
|
+
.join("");
|
|
689
|
+
}
|
|
690
|
+
return {
|
|
691
|
+
...super.toLogEntry(applHost),
|
|
692
|
+
message,
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
Security2CCMessageEncapsulation = __decorate([
|
|
697
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.MessageEncapsulation),
|
|
698
|
+
(0, CommandClass_1.expectedCCResponse)(getCCResponseForMessageEncapsulation, testCCResponseForMessageEncapsulation)
|
|
699
|
+
], Security2CCMessageEncapsulation);
|
|
700
|
+
exports.Security2CCMessageEncapsulation = Security2CCMessageEncapsulation;
|
|
701
|
+
let Security2CCNonceReport = class Security2CCNonceReport extends Security2CC {
|
|
702
|
+
constructor(host, options) {
|
|
703
|
+
super(host, options);
|
|
704
|
+
// Make sure that we can send/receive secure commands
|
|
705
|
+
assertSecurity.call(this, options);
|
|
706
|
+
if ((0, serial_1.gotDeserializationOptions)(options)) {
|
|
707
|
+
(0, core_1.validatePayload)(this.payload.length >= 2);
|
|
708
|
+
this._sequenceNumber = this.payload[0];
|
|
709
|
+
// Don't accept duplicate commands
|
|
710
|
+
validateSequenceNumber.call(this, this._sequenceNumber);
|
|
711
|
+
this.MOS = !!(this.payload[1] & 0b10);
|
|
712
|
+
this.SOS = !!(this.payload[1] & 0b1);
|
|
713
|
+
(0, core_1.validatePayload)(this.MOS || this.SOS);
|
|
714
|
+
if (this.SOS) {
|
|
715
|
+
// If the SOS flag is set, the REI field MUST be included in the command
|
|
716
|
+
(0, core_1.validatePayload)(this.payload.length >= 18);
|
|
717
|
+
this.receiverEI = this.payload.slice(2, 18);
|
|
718
|
+
// In that case we also need to store it, so the next sent command
|
|
719
|
+
// can use it for encryption
|
|
720
|
+
this.host.securityManager2.storeRemoteEI(this.nodeId, this.receiverEI);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
this.SOS = options.SOS;
|
|
725
|
+
this.MOS = options.MOS;
|
|
726
|
+
if (options.SOS)
|
|
727
|
+
this.receiverEI = options.receiverEI;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Return the sequence number of this command.
|
|
732
|
+
*
|
|
733
|
+
* **WARNING:** If the sequence number hasn't been set before, this will create a new one.
|
|
734
|
+
* When sending messages, this should only happen immediately before serializing.
|
|
735
|
+
*/
|
|
736
|
+
get sequenceNumber() {
|
|
737
|
+
if (this._sequenceNumber == undefined) {
|
|
738
|
+
this._sequenceNumber =
|
|
739
|
+
this.host.securityManager2.nextSequenceNumber(this.nodeId);
|
|
740
|
+
}
|
|
741
|
+
return this._sequenceNumber;
|
|
742
|
+
}
|
|
743
|
+
serialize() {
|
|
744
|
+
this.payload = Buffer.from([
|
|
745
|
+
this.sequenceNumber,
|
|
746
|
+
(this.MOS ? 0b10 : 0) + (this.SOS ? 0b1 : 0),
|
|
747
|
+
]);
|
|
748
|
+
if (this.SOS) {
|
|
749
|
+
this.payload = Buffer.concat([this.payload, this.receiverEI]);
|
|
750
|
+
}
|
|
751
|
+
return super.serialize();
|
|
752
|
+
}
|
|
753
|
+
toLogEntry(applHost) {
|
|
754
|
+
const message = {
|
|
755
|
+
"sequence number": this.sequenceNumber,
|
|
756
|
+
SOS: this.SOS,
|
|
757
|
+
MOS: this.MOS,
|
|
758
|
+
};
|
|
759
|
+
if (this.receiverEI) {
|
|
760
|
+
message["receiver entropy"] = (0, safe_1.buffer2hex)(this.receiverEI);
|
|
761
|
+
}
|
|
762
|
+
return {
|
|
763
|
+
...super.toLogEntry(applHost),
|
|
764
|
+
message,
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
Security2CCNonceReport = __decorate([
|
|
769
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.NonceReport)
|
|
770
|
+
], Security2CCNonceReport);
|
|
771
|
+
exports.Security2CCNonceReport = Security2CCNonceReport;
|
|
772
|
+
let Security2CCNonceGet = class Security2CCNonceGet extends Security2CC {
|
|
773
|
+
constructor(host, options) {
|
|
774
|
+
super(host, options);
|
|
775
|
+
// Make sure that we can send/receive secure commands
|
|
776
|
+
assertSecurity.call(this, options);
|
|
777
|
+
if ((0, serial_1.gotDeserializationOptions)(options)) {
|
|
778
|
+
(0, core_1.validatePayload)(this.payload.length >= 1);
|
|
779
|
+
this._sequenceNumber = this.payload[0];
|
|
780
|
+
// Don't accept duplicate commands
|
|
781
|
+
validateSequenceNumber.call(this, this._sequenceNumber);
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
// No options here
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Return the sequence number of this command.
|
|
789
|
+
*
|
|
790
|
+
* **WARNING:** If the sequence number hasn't been set before, this will create a new one.
|
|
791
|
+
* When sending messages, this should only happen immediately before serializing.
|
|
792
|
+
*/
|
|
793
|
+
get sequenceNumber() {
|
|
794
|
+
if (this._sequenceNumber == undefined) {
|
|
795
|
+
this._sequenceNumber =
|
|
796
|
+
this.host.securityManager2.nextSequenceNumber(this.nodeId);
|
|
797
|
+
}
|
|
798
|
+
return this._sequenceNumber;
|
|
799
|
+
}
|
|
800
|
+
serialize() {
|
|
801
|
+
this.payload = Buffer.from([this.sequenceNumber]);
|
|
802
|
+
return super.serialize();
|
|
803
|
+
}
|
|
804
|
+
toLogEntry(applHost) {
|
|
805
|
+
return {
|
|
806
|
+
...super.toLogEntry(applHost),
|
|
807
|
+
message: { "sequence number": this.sequenceNumber },
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
Security2CCNonceGet = __decorate([
|
|
812
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.NonceGet),
|
|
813
|
+
(0, CommandClass_1.expectedCCResponse)(Security2CCNonceReport)
|
|
814
|
+
], Security2CCNonceGet);
|
|
815
|
+
exports.Security2CCNonceGet = Security2CCNonceGet;
|
|
816
|
+
let Security2CCKEXReport = class Security2CCKEXReport extends Security2CC {
|
|
817
|
+
constructor(host, options) {
|
|
818
|
+
super(host, options);
|
|
819
|
+
if ((0, serial_1.gotDeserializationOptions)(options)) {
|
|
820
|
+
(0, core_1.validatePayload)(this.payload.length >= 4);
|
|
821
|
+
this.requestCSA = !!(this.payload[0] & 0b10);
|
|
822
|
+
this.echo = !!(this.payload[0] & 0b1);
|
|
823
|
+
// The bit mask starts at 0, but bit 0 is not used
|
|
824
|
+
this.supportedKEXSchemes = (0, core_1.parseBitMask)(this.payload.slice(1, 2), 0).filter((s) => s !== 0);
|
|
825
|
+
this.supportedECDHProfiles = (0, core_1.parseBitMask)(this.payload.slice(2, 3), shared_1.ECDHProfiles.Curve25519);
|
|
826
|
+
this.requestedKeys = (0, core_1.parseBitMask)(this.payload.slice(3, 4), core_1.SecurityClass.S2_Unauthenticated);
|
|
827
|
+
}
|
|
828
|
+
else {
|
|
829
|
+
this.requestCSA = options.requestCSA;
|
|
830
|
+
this.echo = options.echo;
|
|
831
|
+
this.supportedKEXSchemes = options.supportedKEXSchemes;
|
|
832
|
+
this.supportedECDHProfiles = options.supportedECDHProfiles;
|
|
833
|
+
this.requestedKeys = options.requestedKeys;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
serialize() {
|
|
837
|
+
this.payload = Buffer.concat([
|
|
838
|
+
Buffer.from([(this.requestCSA ? 0b10 : 0) + (this.echo ? 0b1 : 0)]),
|
|
839
|
+
// The bit mask starts at 0, but bit 0 is not used
|
|
840
|
+
(0, core_1.encodeBitMask)(this.supportedKEXSchemes, 7, 0),
|
|
841
|
+
(0, core_1.encodeBitMask)(this.supportedECDHProfiles, 7, shared_1.ECDHProfiles.Curve25519),
|
|
842
|
+
(0, core_1.encodeBitMask)(this.requestedKeys, core_1.SecurityClass.S0_Legacy, core_1.SecurityClass.S2_Unauthenticated),
|
|
843
|
+
]);
|
|
844
|
+
return super.serialize();
|
|
845
|
+
}
|
|
846
|
+
toLogEntry(applHost) {
|
|
847
|
+
return {
|
|
848
|
+
...super.toLogEntry(applHost),
|
|
849
|
+
message: {
|
|
850
|
+
echo: this.echo,
|
|
851
|
+
"supported schemes": this.supportedKEXSchemes
|
|
852
|
+
.map((s) => `\n· ${(0, safe_1.getEnumMemberName)(shared_1.KEXSchemes, s)}`)
|
|
853
|
+
.join(""),
|
|
854
|
+
"supported ECDH profiles": this.supportedECDHProfiles
|
|
855
|
+
.map((s) => `\n· ${(0, safe_1.getEnumMemberName)(shared_1.ECDHProfiles, s)}`)
|
|
856
|
+
.join(""),
|
|
857
|
+
"CSA requested": this.requestCSA,
|
|
858
|
+
"requested security classes": this.requestedKeys
|
|
859
|
+
.map((s) => `\n· ${(0, safe_1.getEnumMemberName)(core_1.SecurityClass, s)}`)
|
|
860
|
+
.join(""),
|
|
861
|
+
},
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
Security2CCKEXReport = __decorate([
|
|
866
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.KEXReport)
|
|
867
|
+
], Security2CCKEXReport);
|
|
868
|
+
exports.Security2CCKEXReport = Security2CCKEXReport;
|
|
869
|
+
let Security2CCKEXGet = class Security2CCKEXGet extends Security2CC {
|
|
870
|
+
};
|
|
871
|
+
Security2CCKEXGet = __decorate([
|
|
872
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.KEXGet),
|
|
873
|
+
(0, CommandClass_1.expectedCCResponse)(Security2CCKEXReport)
|
|
874
|
+
], Security2CCKEXGet);
|
|
875
|
+
exports.Security2CCKEXGet = Security2CCKEXGet;
|
|
876
|
+
let Security2CCKEXSet = class Security2CCKEXSet extends Security2CC {
|
|
877
|
+
constructor(host, options) {
|
|
878
|
+
super(host, options);
|
|
879
|
+
if ((0, serial_1.gotDeserializationOptions)(options)) {
|
|
880
|
+
(0, core_1.validatePayload)(this.payload.length >= 4);
|
|
881
|
+
this.permitCSA = !!(this.payload[0] & 0b10);
|
|
882
|
+
this.echo = !!(this.payload[0] & 0b1);
|
|
883
|
+
// The bit mask starts at 0, but bit 0 is not used
|
|
884
|
+
const selectedKEXSchemes = (0, core_1.parseBitMask)(this.payload.slice(1, 2), 0).filter((s) => s !== 0);
|
|
885
|
+
(0, core_1.validatePayload)(selectedKEXSchemes.length === 1);
|
|
886
|
+
this.selectedKEXScheme = selectedKEXSchemes[0];
|
|
887
|
+
const selectedECDHProfiles = (0, core_1.parseBitMask)(this.payload.slice(2, 3), shared_1.ECDHProfiles.Curve25519);
|
|
888
|
+
(0, core_1.validatePayload)(selectedECDHProfiles.length === 1);
|
|
889
|
+
this.selectedECDHProfile = selectedECDHProfiles[0];
|
|
890
|
+
this.grantedKeys = (0, core_1.parseBitMask)(this.payload.slice(3, 4), core_1.SecurityClass.S2_Unauthenticated);
|
|
891
|
+
}
|
|
892
|
+
else {
|
|
893
|
+
this.permitCSA = options.permitCSA;
|
|
894
|
+
this.echo = options.echo;
|
|
895
|
+
this.selectedKEXScheme = options.selectedKEXScheme;
|
|
896
|
+
this.selectedECDHProfile = options.selectedECDHProfile;
|
|
897
|
+
this.grantedKeys = options.grantedKeys;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
serialize() {
|
|
901
|
+
this.payload = Buffer.concat([
|
|
902
|
+
Buffer.from([(this.permitCSA ? 0b10 : 0) + (this.echo ? 0b1 : 0)]),
|
|
903
|
+
// The bit mask starts at 0, but bit 0 is not used
|
|
904
|
+
(0, core_1.encodeBitMask)([this.selectedKEXScheme], 7, 0),
|
|
905
|
+
(0, core_1.encodeBitMask)([this.selectedECDHProfile], 7, shared_1.ECDHProfiles.Curve25519),
|
|
906
|
+
(0, core_1.encodeBitMask)(this.grantedKeys, core_1.SecurityClass.S0_Legacy, core_1.SecurityClass.S2_Unauthenticated),
|
|
907
|
+
]);
|
|
908
|
+
return super.serialize();
|
|
909
|
+
}
|
|
910
|
+
toLogEntry(applHost) {
|
|
911
|
+
return {
|
|
912
|
+
...super.toLogEntry(applHost),
|
|
913
|
+
message: {
|
|
914
|
+
echo: this.echo,
|
|
915
|
+
"selected scheme": (0, safe_1.getEnumMemberName)(shared_1.KEXSchemes, this.selectedKEXScheme),
|
|
916
|
+
"selected ECDH profile": (0, safe_1.getEnumMemberName)(shared_1.ECDHProfiles, this.selectedECDHProfile),
|
|
917
|
+
"CSA permitted": this.permitCSA,
|
|
918
|
+
"granted security classes": this.grantedKeys
|
|
919
|
+
.map((s) => `\n· ${(0, safe_1.getEnumMemberName)(core_1.SecurityClass, s)}`)
|
|
920
|
+
.join(""),
|
|
921
|
+
},
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
Security2CCKEXSet = __decorate([
|
|
926
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.KEXSet)
|
|
927
|
+
], Security2CCKEXSet);
|
|
928
|
+
exports.Security2CCKEXSet = Security2CCKEXSet;
|
|
929
|
+
let Security2CCKEXFail = class Security2CCKEXFail extends Security2CC {
|
|
930
|
+
constructor(host, options) {
|
|
931
|
+
super(host, options);
|
|
932
|
+
if ((0, serial_1.gotDeserializationOptions)(options)) {
|
|
933
|
+
(0, core_1.validatePayload)(this.payload.length >= 1);
|
|
934
|
+
this.failType = this.payload[0];
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
this.failType = options.failType;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
serialize() {
|
|
941
|
+
this.payload = Buffer.from([this.failType]);
|
|
942
|
+
return super.serialize();
|
|
943
|
+
}
|
|
944
|
+
toLogEntry(applHost) {
|
|
945
|
+
return {
|
|
946
|
+
...super.toLogEntry(applHost),
|
|
947
|
+
message: { reason: (0, safe_1.getEnumMemberName)(shared_1.KEXFailType, this.failType) },
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
Security2CCKEXFail = __decorate([
|
|
952
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.KEXFail)
|
|
953
|
+
], Security2CCKEXFail);
|
|
954
|
+
exports.Security2CCKEXFail = Security2CCKEXFail;
|
|
955
|
+
let Security2CCPublicKeyReport = class Security2CCPublicKeyReport extends Security2CC {
|
|
956
|
+
constructor(host, options) {
|
|
957
|
+
super(host, options);
|
|
958
|
+
if ((0, serial_1.gotDeserializationOptions)(options)) {
|
|
959
|
+
(0, core_1.validatePayload)(this.payload.length >= 17);
|
|
960
|
+
this.includingNode = !!(this.payload[0] & 0b1);
|
|
961
|
+
this.publicKey = this.payload.slice(1);
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
this.includingNode = options.includingNode;
|
|
965
|
+
this.publicKey = options.publicKey;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
serialize() {
|
|
969
|
+
this.payload = Buffer.concat([
|
|
970
|
+
Buffer.from([this.includingNode ? 1 : 0]),
|
|
971
|
+
this.publicKey,
|
|
972
|
+
]);
|
|
973
|
+
return super.serialize();
|
|
974
|
+
}
|
|
975
|
+
toLogEntry(applHost) {
|
|
976
|
+
return {
|
|
977
|
+
...super.toLogEntry(applHost),
|
|
978
|
+
message: {
|
|
979
|
+
"is including node": this.includingNode,
|
|
980
|
+
"public key": (0, safe_1.buffer2hex)(this.publicKey),
|
|
981
|
+
},
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
};
|
|
985
|
+
Security2CCPublicKeyReport = __decorate([
|
|
986
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.PublicKeyReport)
|
|
987
|
+
], Security2CCPublicKeyReport);
|
|
988
|
+
exports.Security2CCPublicKeyReport = Security2CCPublicKeyReport;
|
|
989
|
+
let Security2CCNetworkKeyReport = class Security2CCNetworkKeyReport extends Security2CC {
|
|
990
|
+
constructor(host, options) {
|
|
991
|
+
super(host, options);
|
|
992
|
+
if ((0, serial_1.gotDeserializationOptions)(options)) {
|
|
993
|
+
// TODO: Deserialize payload
|
|
994
|
+
throw new core_1.ZWaveError(`${this.constructor.name}: deserialization not implemented`, core_1.ZWaveErrorCodes.Deserialization_NotImplemented);
|
|
995
|
+
}
|
|
996
|
+
else {
|
|
997
|
+
this.grantedKey = options.grantedKey;
|
|
998
|
+
this.networkKey = options.networkKey;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
serialize() {
|
|
1002
|
+
this.payload = Buffer.concat([
|
|
1003
|
+
securityClassToBitMask(this.grantedKey),
|
|
1004
|
+
this.networkKey,
|
|
1005
|
+
]);
|
|
1006
|
+
return super.serialize();
|
|
1007
|
+
}
|
|
1008
|
+
toLogEntry(applHost) {
|
|
1009
|
+
return {
|
|
1010
|
+
...super.toLogEntry(applHost),
|
|
1011
|
+
message: {
|
|
1012
|
+
"security class": (0, safe_1.getEnumMemberName)(core_1.SecurityClass, this.grantedKey),
|
|
1013
|
+
// This shouldn't be logged, so users can safely post their logs online
|
|
1014
|
+
// "network key": buffer2hex(this.networkKey),
|
|
1015
|
+
},
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
Security2CCNetworkKeyReport = __decorate([
|
|
1020
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.NetworkKeyReport)
|
|
1021
|
+
], Security2CCNetworkKeyReport);
|
|
1022
|
+
exports.Security2CCNetworkKeyReport = Security2CCNetworkKeyReport;
|
|
1023
|
+
let Security2CCNetworkKeyGet = class Security2CCNetworkKeyGet extends Security2CC {
|
|
1024
|
+
constructor(host, options) {
|
|
1025
|
+
super(host, options);
|
|
1026
|
+
if ((0, serial_1.gotDeserializationOptions)(options)) {
|
|
1027
|
+
(0, core_1.validatePayload)(this.payload.length >= 1);
|
|
1028
|
+
this.requestedKey = bitMaskToSecurityClass(this.payload, 0);
|
|
1029
|
+
}
|
|
1030
|
+
else {
|
|
1031
|
+
this.requestedKey = options.requestedKey;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
serialize() {
|
|
1035
|
+
this.payload = securityClassToBitMask(this.requestedKey);
|
|
1036
|
+
return super.serialize();
|
|
1037
|
+
}
|
|
1038
|
+
toLogEntry(applHost) {
|
|
1039
|
+
return {
|
|
1040
|
+
...super.toLogEntry(applHost),
|
|
1041
|
+
message: {
|
|
1042
|
+
"security class": (0, safe_1.getEnumMemberName)(core_1.SecurityClass, this.requestedKey),
|
|
1043
|
+
},
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
Security2CCNetworkKeyGet = __decorate([
|
|
1048
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.NetworkKeyGet),
|
|
1049
|
+
(0, CommandClass_1.expectedCCResponse)(Security2CCNetworkKeyReport)
|
|
1050
|
+
], Security2CCNetworkKeyGet);
|
|
1051
|
+
exports.Security2CCNetworkKeyGet = Security2CCNetworkKeyGet;
|
|
1052
|
+
let Security2CCNetworkKeyVerify = class Security2CCNetworkKeyVerify extends Security2CC {
|
|
1053
|
+
};
|
|
1054
|
+
Security2CCNetworkKeyVerify = __decorate([
|
|
1055
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.NetworkKeyVerify)
|
|
1056
|
+
], Security2CCNetworkKeyVerify);
|
|
1057
|
+
exports.Security2CCNetworkKeyVerify = Security2CCNetworkKeyVerify;
|
|
1058
|
+
let Security2CCTransferEnd = class Security2CCTransferEnd extends Security2CC {
|
|
1059
|
+
constructor(host, options) {
|
|
1060
|
+
super(host, options);
|
|
1061
|
+
if ((0, serial_1.gotDeserializationOptions)(options)) {
|
|
1062
|
+
(0, core_1.validatePayload)(this.payload.length >= 1);
|
|
1063
|
+
this.keyVerified = !!(this.payload[0] & 0b10);
|
|
1064
|
+
this.keyRequestComplete = !!(this.payload[0] & 0b1);
|
|
1065
|
+
}
|
|
1066
|
+
else {
|
|
1067
|
+
this.keyVerified = options.keyVerified;
|
|
1068
|
+
this.keyRequestComplete = options.keyRequestComplete;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
serialize() {
|
|
1072
|
+
this.payload = Buffer.from([
|
|
1073
|
+
(this.keyVerified ? 0b10 : 0) + (this.keyRequestComplete ? 0b1 : 0),
|
|
1074
|
+
]);
|
|
1075
|
+
return super.serialize();
|
|
1076
|
+
}
|
|
1077
|
+
toLogEntry(applHost) {
|
|
1078
|
+
return {
|
|
1079
|
+
...super.toLogEntry(applHost),
|
|
1080
|
+
message: {
|
|
1081
|
+
"key verified": this.keyVerified,
|
|
1082
|
+
"request complete": this.keyRequestComplete,
|
|
1083
|
+
},
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
Security2CCTransferEnd = __decorate([
|
|
1088
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.TransferEnd)
|
|
1089
|
+
], Security2CCTransferEnd);
|
|
1090
|
+
exports.Security2CCTransferEnd = Security2CCTransferEnd;
|
|
1091
|
+
let Security2CCCommandsSupportedReport = class Security2CCCommandsSupportedReport extends Security2CC {
|
|
1092
|
+
constructor(host, options) {
|
|
1093
|
+
super(host, options);
|
|
1094
|
+
const CCs = (0, core_1.parseCCList)(this.payload);
|
|
1095
|
+
// SDS13783: A sending node MAY terminate the list of supported command classes with the
|
|
1096
|
+
// COMMAND_CLASS_MARK command class identifier.
|
|
1097
|
+
// A receiving node MUST stop parsing the list of supported command classes if it detects the
|
|
1098
|
+
// COMMAND_CLASS_MARK command class identifier in the Security 2 Commands Supported Report
|
|
1099
|
+
this.supportedCCs = CCs.supportedCCs;
|
|
1100
|
+
}
|
|
1101
|
+
toLogEntry(applHost) {
|
|
1102
|
+
return {
|
|
1103
|
+
...super.toLogEntry(applHost),
|
|
1104
|
+
message: {
|
|
1105
|
+
supportedCCs: this.supportedCCs
|
|
1106
|
+
.map((cc) => (0, core_1.getCCName)(cc))
|
|
1107
|
+
.map((cc) => `\n· ${cc}`)
|
|
1108
|
+
.join(""),
|
|
1109
|
+
},
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
Security2CCCommandsSupportedReport = __decorate([
|
|
1114
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.CommandsSupportedReport)
|
|
1115
|
+
], Security2CCCommandsSupportedReport);
|
|
1116
|
+
exports.Security2CCCommandsSupportedReport = Security2CCCommandsSupportedReport;
|
|
1117
|
+
let Security2CCCommandsSupportedGet = class Security2CCCommandsSupportedGet extends Security2CC {
|
|
1118
|
+
};
|
|
1119
|
+
Security2CCCommandsSupportedGet = __decorate([
|
|
1120
|
+
(0, CommandClass_1.CCCommand)(_Types_1.Security2Command.CommandsSupportedGet),
|
|
1121
|
+
(0, CommandClass_1.expectedCCResponse)(Security2CCCommandsSupportedReport)
|
|
1122
|
+
], Security2CCCommandsSupportedGet);
|
|
1123
|
+
exports.Security2CCCommandsSupportedGet = Security2CCCommandsSupportedGet;
|
|
1124
|
+
//# sourceMappingURL=Security2CC.js.map
|