@zwave-js/cc 14.1.0 → 14.2.0
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/cjs/cc/AlarmSensorCC.js.map +2 -2
- package/build/cjs/cc/AssociationCC.d.ts +2 -0
- package/build/cjs/cc/AssociationCC.js +5 -0
- package/build/cjs/cc/AssociationCC.js.map +2 -2
- package/build/cjs/cc/AssociationGroupInfoCC.js.map +2 -2
- package/build/cjs/cc/BarrierOperatorCC.js.map +2 -2
- package/build/cjs/cc/BasicCC.js.map +2 -2
- package/build/cjs/cc/BatteryCC.js.map +2 -2
- package/build/cjs/cc/BinarySensorCC.js.map +2 -2
- package/build/cjs/cc/BinarySwitchCC.js.map +2 -2
- package/build/cjs/cc/CRC16CC.d.ts +4 -0
- package/build/cjs/cc/CRC16CC.js +23 -0
- package/build/cjs/cc/CRC16CC.js.map +2 -2
- package/build/cjs/cc/CentralSceneCC.js.map +2 -2
- package/build/cjs/cc/ClimateControlScheduleCC.js.map +2 -2
- package/build/cjs/cc/ClockCC.js.map +2 -2
- package/build/cjs/cc/ColorSwitchCC.js.map +2 -2
- package/build/cjs/cc/ConfigurationCC.d.ts +4 -0
- package/build/cjs/cc/ConfigurationCC.js +10 -0
- package/build/cjs/cc/ConfigurationCC.js.map +2 -2
- package/build/cjs/cc/DoorLockCC.js.map +2 -2
- package/build/cjs/cc/DoorLockLoggingCC.js.map +2 -2
- package/build/cjs/cc/EnergyProductionCC.js.map +2 -2
- package/build/cjs/cc/EntryControlCC.js.map +2 -2
- package/build/cjs/cc/FirmwareUpdateMetaDataCC.js.map +2 -2
- package/build/cjs/cc/HumidityControlModeCC.js.map +2 -2
- package/build/cjs/cc/HumidityControlSetpointCC.js.map +2 -2
- package/build/cjs/cc/InclusionControllerCC.js.map +2 -2
- package/build/cjs/cc/IndicatorCC.js.map +2 -2
- package/build/cjs/cc/IrrigationCC.js.map +2 -2
- package/build/cjs/cc/LanguageCC.js.map +2 -2
- package/build/cjs/cc/LockCC.js.map +2 -2
- package/build/cjs/cc/ManufacturerProprietaryCC.js.map +2 -2
- package/build/cjs/cc/ManufacturerSpecificCC.js.map +2 -2
- package/build/cjs/cc/MeterCC.js.map +2 -2
- package/build/cjs/cc/MultiChannelAssociationCC.d.ts +2 -0
- package/build/cjs/cc/MultiChannelAssociationCC.js +6 -0
- package/build/cjs/cc/MultiChannelAssociationCC.js.map +2 -2
- package/build/cjs/cc/MultiChannelCC.d.ts +10 -0
- package/build/cjs/cc/MultiChannelCC.js +65 -0
- package/build/cjs/cc/MultiChannelCC.js.map +2 -2
- package/build/cjs/cc/MultiCommandCC.d.ts +4 -0
- package/build/cjs/cc/MultiCommandCC.js +34 -1
- package/build/cjs/cc/MultiCommandCC.js.map +2 -2
- package/build/cjs/cc/MultilevelSensorCC.js.map +2 -2
- package/build/cjs/cc/MultilevelSwitchCC.js.map +2 -2
- package/build/cjs/cc/NodeNamingCC.js.map +2 -2
- package/build/cjs/cc/NotificationCC.js.map +2 -2
- package/build/cjs/cc/PowerlevelCC.js.map +2 -2
- package/build/cjs/cc/ProtectionCC.js.map +2 -2
- package/build/cjs/cc/SceneActivationCC.js.map +2 -2
- package/build/cjs/cc/SceneActuatorConfigurationCC.js.map +2 -2
- package/build/cjs/cc/SceneControllerConfigurationCC.js.map +2 -2
- package/build/cjs/cc/ScheduleEntryLockCC.js.map +2 -2
- package/build/cjs/cc/Security2CC.d.ts +4 -0
- package/build/cjs/cc/Security2CC.js +215 -0
- package/build/cjs/cc/Security2CC.js.map +2 -2
- package/build/cjs/cc/SecurityCC.d.ts +6 -0
- package/build/cjs/cc/SecurityCC.js +53 -0
- package/build/cjs/cc/SecurityCC.js.map +2 -2
- package/build/cjs/cc/SoundSwitchCC.js.map +2 -2
- package/build/cjs/cc/SupervisionCC.d.ts +4 -0
- package/build/cjs/cc/SupervisionCC.js +25 -0
- package/build/cjs/cc/SupervisionCC.js.map +2 -2
- package/build/cjs/cc/ThermostatFanModeCC.js.map +2 -2
- package/build/cjs/cc/ThermostatModeCC.js.map +2 -2
- package/build/cjs/cc/ThermostatSetbackCC.js.map +2 -2
- package/build/cjs/cc/ThermostatSetpointCC.js.map +2 -2
- package/build/cjs/cc/TimeCC.js.map +2 -2
- package/build/cjs/cc/TimeParametersCC.js.map +2 -2
- package/build/cjs/cc/TransportServiceCC.d.ts +5 -0
- package/build/cjs/cc/TransportServiceCC.js +13 -0
- package/build/cjs/cc/TransportServiceCC.js.map +2 -2
- package/build/cjs/cc/UserCodeCC.js.map +2 -2
- package/build/cjs/cc/VersionCC.js.map +2 -2
- package/build/cjs/cc/WakeUpCC.js.map +2 -2
- package/build/cjs/cc/WindowCoveringCC.js.map +2 -2
- package/build/cjs/cc/ZWavePlusCC.js.map +2 -2
- package/build/cjs/cc/ZWaveProtocolCC.js.map +2 -2
- package/build/cjs/cc/manufacturerProprietary/Decorators.js +6 -5
- package/build/cjs/cc/manufacturerProprietary/Decorators.js.map +2 -2
- package/build/cjs/cc/manufacturerProprietary/FibaroCC.js.map +2 -2
- package/build/cjs/lib/CommandClass.d.ts +15 -1
- package/build/cjs/lib/CommandClass.js +56 -1
- package/build/cjs/lib/CommandClass.js.map +2 -2
- package/build/cjs/lib/CommandClassDecorators.js +7 -6
- package/build/cjs/lib/CommandClassDecorators.js.map +2 -2
- package/build/cjs/lib/Security2/Extension.js +2 -2
- package/build/cjs/lib/Security2/Extension.js.map +2 -2
- package/build/esm/cc/AlarmSensorCC.d.ts.map +1 -1
- package/build/esm/cc/AlarmSensorCC.js +1 -0
- package/build/esm/cc/AlarmSensorCC.js.map +1 -1
- package/build/esm/cc/AssociationCC.d.ts +2 -0
- package/build/esm/cc/AssociationCC.d.ts.map +1 -1
- package/build/esm/cc/AssociationCC.js +14 -0
- package/build/esm/cc/AssociationCC.js.map +1 -1
- package/build/esm/cc/AssociationGroupInfoCC.d.ts.map +1 -1
- package/build/esm/cc/AssociationGroupInfoCC.js +6 -0
- package/build/esm/cc/AssociationGroupInfoCC.js.map +1 -1
- package/build/esm/cc/BarrierOperatorCC.d.ts.map +1 -1
- package/build/esm/cc/BarrierOperatorCC.js +3 -0
- package/build/esm/cc/BarrierOperatorCC.js.map +1 -1
- package/build/esm/cc/BasicCC.d.ts.map +1 -1
- package/build/esm/cc/BasicCC.js +2 -0
- package/build/esm/cc/BasicCC.js.map +1 -1
- package/build/esm/cc/BatteryCC.d.ts.map +1 -1
- package/build/esm/cc/BatteryCC.js +1 -0
- package/build/esm/cc/BatteryCC.js.map +1 -1
- package/build/esm/cc/BinarySensorCC.d.ts.map +1 -1
- package/build/esm/cc/BinarySensorCC.js +3 -0
- package/build/esm/cc/BinarySensorCC.js.map +1 -1
- package/build/esm/cc/BinarySwitchCC.d.ts.map +1 -1
- package/build/esm/cc/BinarySwitchCC.js +2 -0
- package/build/esm/cc/BinarySwitchCC.js.map +1 -1
- package/build/esm/cc/CRC16CC.d.ts +4 -0
- package/build/esm/cc/CRC16CC.d.ts.map +1 -1
- package/build/esm/cc/CRC16CC.js +30 -0
- package/build/esm/cc/CRC16CC.js.map +1 -1
- package/build/esm/cc/CentralSceneCC.d.ts.map +1 -1
- package/build/esm/cc/CentralSceneCC.js +1 -0
- package/build/esm/cc/CentralSceneCC.js.map +1 -1
- package/build/esm/cc/ClimateControlScheduleCC.d.ts.map +1 -1
- package/build/esm/cc/ClimateControlScheduleCC.js +3 -0
- package/build/esm/cc/ClimateControlScheduleCC.js.map +1 -1
- package/build/esm/cc/ClockCC.d.ts.map +1 -1
- package/build/esm/cc/ClockCC.js +1 -0
- package/build/esm/cc/ClockCC.js.map +1 -1
- package/build/esm/cc/ColorSwitchCC.d.ts.map +1 -1
- package/build/esm/cc/ColorSwitchCC.js +6 -0
- package/build/esm/cc/ColorSwitchCC.js.map +1 -1
- package/build/esm/cc/ConfigurationCC.d.ts +4 -0
- package/build/esm/cc/ConfigurationCC.d.ts.map +1 -1
- package/build/esm/cc/ConfigurationCC.js +27 -0
- package/build/esm/cc/ConfigurationCC.js.map +1 -1
- package/build/esm/cc/DoorLockCC.d.ts.map +1 -1
- package/build/esm/cc/DoorLockCC.js +2 -0
- package/build/esm/cc/DoorLockCC.js.map +1 -1
- package/build/esm/cc/DoorLockLoggingCC.d.ts.map +1 -1
- package/build/esm/cc/DoorLockLoggingCC.js +1 -0
- package/build/esm/cc/DoorLockLoggingCC.js.map +1 -1
- package/build/esm/cc/EnergyProductionCC.d.ts.map +1 -1
- package/build/esm/cc/EnergyProductionCC.js +2 -0
- package/build/esm/cc/EnergyProductionCC.js.map +1 -1
- package/build/esm/cc/EntryControlCC.d.ts.map +1 -1
- package/build/esm/cc/EntryControlCC.js +1 -0
- package/build/esm/cc/EntryControlCC.js.map +1 -1
- package/build/esm/cc/FirmwareUpdateMetaDataCC.d.ts.map +1 -1
- package/build/esm/cc/FirmwareUpdateMetaDataCC.js +5 -0
- package/build/esm/cc/FirmwareUpdateMetaDataCC.js.map +1 -1
- package/build/esm/cc/HumidityControlModeCC.d.ts.map +1 -1
- package/build/esm/cc/HumidityControlModeCC.js +1 -0
- package/build/esm/cc/HumidityControlModeCC.js.map +1 -1
- package/build/esm/cc/HumidityControlSetpointCC.d.ts.map +1 -1
- package/build/esm/cc/HumidityControlSetpointCC.js +4 -0
- package/build/esm/cc/HumidityControlSetpointCC.js.map +1 -1
- package/build/esm/cc/InclusionControllerCC.d.ts.map +1 -1
- package/build/esm/cc/InclusionControllerCC.js +2 -0
- package/build/esm/cc/InclusionControllerCC.js.map +1 -1
- package/build/esm/cc/IndicatorCC.d.ts.map +1 -1
- package/build/esm/cc/IndicatorCC.js +7 -0
- package/build/esm/cc/IndicatorCC.js.map +1 -1
- package/build/esm/cc/IrrigationCC.d.ts.map +1 -1
- package/build/esm/cc/IrrigationCC.js +9 -0
- package/build/esm/cc/IrrigationCC.js.map +1 -1
- package/build/esm/cc/LanguageCC.d.ts.map +1 -1
- package/build/esm/cc/LanguageCC.js +1 -0
- package/build/esm/cc/LanguageCC.js.map +1 -1
- package/build/esm/cc/LockCC.d.ts.map +1 -1
- package/build/esm/cc/LockCC.js +1 -0
- package/build/esm/cc/LockCC.js.map +1 -1
- package/build/esm/cc/ManufacturerProprietaryCC.d.ts.map +1 -1
- package/build/esm/cc/ManufacturerProprietaryCC.js +2 -0
- package/build/esm/cc/ManufacturerProprietaryCC.js.map +1 -1
- package/build/esm/cc/ManufacturerSpecificCC.d.ts.map +1 -1
- package/build/esm/cc/ManufacturerSpecificCC.js +2 -0
- package/build/esm/cc/ManufacturerSpecificCC.js.map +1 -1
- package/build/esm/cc/MeterCC.d.ts.map +1 -1
- package/build/esm/cc/MeterCC.js +4 -0
- package/build/esm/cc/MeterCC.js.map +1 -1
- package/build/esm/cc/MultiChannelAssociationCC.d.ts +2 -0
- package/build/esm/cc/MultiChannelAssociationCC.d.ts.map +1 -1
- package/build/esm/cc/MultiChannelAssociationCC.js +17 -0
- package/build/esm/cc/MultiChannelAssociationCC.js.map +1 -1
- package/build/esm/cc/MultiChannelCC.d.ts +10 -0
- package/build/esm/cc/MultiChannelCC.d.ts.map +1 -1
- package/build/esm/cc/MultiChannelCC.js +94 -0
- package/build/esm/cc/MultiChannelCC.js.map +1 -1
- package/build/esm/cc/MultiCommandCC.d.ts +4 -0
- package/build/esm/cc/MultiCommandCC.d.ts.map +1 -1
- package/build/esm/cc/MultiCommandCC.js +35 -1
- package/build/esm/cc/MultiCommandCC.js.map +1 -1
- package/build/esm/cc/MultilevelSensorCC.d.ts.map +1 -1
- package/build/esm/cc/MultilevelSensorCC.js +5 -0
- package/build/esm/cc/MultilevelSensorCC.js.map +1 -1
- package/build/esm/cc/MultilevelSwitchCC.d.ts.map +1 -1
- package/build/esm/cc/MultilevelSwitchCC.js +4 -0
- package/build/esm/cc/MultilevelSwitchCC.js.map +1 -1
- package/build/esm/cc/NodeNamingCC.d.ts.map +1 -1
- package/build/esm/cc/NodeNamingCC.js +2 -0
- package/build/esm/cc/NodeNamingCC.js.map +1 -1
- package/build/esm/cc/NotificationCC.d.ts.map +1 -1
- package/build/esm/cc/NotificationCC.js +9 -0
- package/build/esm/cc/NotificationCC.js.map +1 -1
- package/build/esm/cc/PowerlevelCC.d.ts.map +1 -1
- package/build/esm/cc/PowerlevelCC.js +4 -0
- package/build/esm/cc/PowerlevelCC.js.map +1 -1
- package/build/esm/cc/ProtectionCC.d.ts.map +1 -1
- package/build/esm/cc/ProtectionCC.js +3 -0
- package/build/esm/cc/ProtectionCC.js.map +1 -1
- package/build/esm/cc/SceneActivationCC.d.ts.map +1 -1
- package/build/esm/cc/SceneActivationCC.js +1 -0
- package/build/esm/cc/SceneActivationCC.js.map +1 -1
- package/build/esm/cc/SceneActuatorConfigurationCC.d.ts.map +1 -1
- package/build/esm/cc/SceneActuatorConfigurationCC.js +2 -0
- package/build/esm/cc/SceneActuatorConfigurationCC.js.map +1 -1
- package/build/esm/cc/SceneControllerConfigurationCC.d.ts.map +1 -1
- package/build/esm/cc/SceneControllerConfigurationCC.js +2 -0
- package/build/esm/cc/SceneControllerConfigurationCC.js.map +1 -1
- package/build/esm/cc/ScheduleEntryLockCC.d.ts.map +1 -1
- package/build/esm/cc/ScheduleEntryLockCC.js +14 -0
- package/build/esm/cc/ScheduleEntryLockCC.js.map +1 -1
- package/build/esm/cc/Security2CC.d.ts +4 -0
- package/build/esm/cc/Security2CC.d.ts.map +1 -1
- package/build/esm/cc/Security2CC.js +301 -0
- package/build/esm/cc/Security2CC.js.map +1 -1
- package/build/esm/cc/SecurityCC.d.ts +6 -0
- package/build/esm/cc/SecurityCC.d.ts.map +1 -1
- package/build/esm/cc/SecurityCC.js +73 -0
- package/build/esm/cc/SecurityCC.js.map +1 -1
- package/build/esm/cc/SoundSwitchCC.d.ts.map +1 -1
- package/build/esm/cc/SoundSwitchCC.js +7 -0
- package/build/esm/cc/SoundSwitchCC.js.map +1 -1
- package/build/esm/cc/SupervisionCC.d.ts +4 -0
- package/build/esm/cc/SupervisionCC.d.ts.map +1 -1
- package/build/esm/cc/SupervisionCC.js +30 -0
- package/build/esm/cc/SupervisionCC.js.map +1 -1
- package/build/esm/cc/ThermostatFanModeCC.d.ts.map +1 -1
- package/build/esm/cc/ThermostatFanModeCC.js +1 -0
- package/build/esm/cc/ThermostatFanModeCC.js.map +1 -1
- package/build/esm/cc/ThermostatModeCC.d.ts.map +1 -1
- package/build/esm/cc/ThermostatModeCC.js +3 -0
- package/build/esm/cc/ThermostatModeCC.js.map +1 -1
- package/build/esm/cc/ThermostatSetbackCC.d.ts.map +1 -1
- package/build/esm/cc/ThermostatSetbackCC.js +2 -0
- package/build/esm/cc/ThermostatSetbackCC.js.map +1 -1
- package/build/esm/cc/ThermostatSetpointCC.d.ts.map +1 -1
- package/build/esm/cc/ThermostatSetpointCC.js +6 -0
- package/build/esm/cc/ThermostatSetpointCC.js.map +1 -1
- package/build/esm/cc/TimeCC.d.ts.map +1 -1
- package/build/esm/cc/TimeCC.js +4 -0
- package/build/esm/cc/TimeCC.js.map +1 -1
- package/build/esm/cc/TimeParametersCC.d.ts.map +1 -1
- package/build/esm/cc/TimeParametersCC.js +1 -0
- package/build/esm/cc/TimeParametersCC.js.map +1 -1
- package/build/esm/cc/TransportServiceCC.d.ts +5 -0
- package/build/esm/cc/TransportServiceCC.d.ts.map +1 -1
- package/build/esm/cc/TransportServiceCC.js +24 -0
- package/build/esm/cc/TransportServiceCC.js.map +1 -1
- package/build/esm/cc/UserCodeCC.d.ts.map +1 -1
- package/build/esm/cc/UserCodeCC.js +12 -0
- package/build/esm/cc/UserCodeCC.js.map +1 -1
- package/build/esm/cc/VersionCC.d.ts.map +1 -1
- package/build/esm/cc/VersionCC.js +4 -0
- package/build/esm/cc/VersionCC.js.map +1 -1
- package/build/esm/cc/WakeUpCC.d.ts.map +1 -1
- package/build/esm/cc/WakeUpCC.js +1 -0
- package/build/esm/cc/WakeUpCC.js.map +1 -1
- package/build/esm/cc/WindowCoveringCC.d.ts.map +1 -1
- package/build/esm/cc/WindowCoveringCC.js +5 -0
- package/build/esm/cc/WindowCoveringCC.js.map +1 -1
- package/build/esm/cc/ZWavePlusCC.d.ts.map +1 -1
- package/build/esm/cc/ZWavePlusCC.js +1 -0
- package/build/esm/cc/ZWavePlusCC.js.map +1 -1
- package/build/esm/cc/ZWaveProtocolCC.d.ts.map +1 -1
- package/build/esm/cc/ZWaveProtocolCC.js +27 -0
- package/build/esm/cc/ZWaveProtocolCC.js.map +1 -1
- package/build/esm/cc/manufacturerProprietary/Decorators.d.ts.map +1 -1
- package/build/esm/cc/manufacturerProprietary/Decorators.js +2 -1
- package/build/esm/cc/manufacturerProprietary/Decorators.js.map +1 -1
- package/build/esm/cc/manufacturerProprietary/FibaroCC.d.ts.map +1 -1
- package/build/esm/cc/manufacturerProprietary/FibaroCC.js +3 -0
- package/build/esm/cc/manufacturerProprietary/FibaroCC.js.map +1 -1
- package/build/esm/lib/CommandClass.d.ts +15 -1
- package/build/esm/lib/CommandClass.d.ts.map +1 -1
- package/build/esm/lib/CommandClass.js +94 -1
- package/build/esm/lib/CommandClass.js.map +1 -1
- package/build/esm/lib/CommandClassDecorators.d.ts.map +1 -1
- package/build/esm/lib/CommandClassDecorators.js +2 -1
- package/build/esm/lib/CommandClassDecorators.js.map +1 -1
- package/build/esm/lib/Security2/Extension.js +1 -1
- package/build/esm/lib/Security2/Extension.js.map +1 -1
- package/package.json +4 -4
|
@@ -922,6 +922,7 @@ let Security2CCMessageEncapsulation = (() => {
|
|
|
922
922
|
throw new ZWaveError("Multicast Security S2 encapsulation requires the MGRP extension", ZWaveErrorCodes.Security2CC_MissingExtension);
|
|
923
923
|
}
|
|
924
924
|
}
|
|
925
|
+
/** @deprecated Use {@link fromAsync} instead */
|
|
925
926
|
static from(raw, ctx) {
|
|
926
927
|
const securityManager = assertSecurityRX(ctx);
|
|
927
928
|
validatePayload(raw.payload.length >= 2);
|
|
@@ -1131,6 +1132,7 @@ let Security2CCMessageEncapsulation = (() => {
|
|
|
1131
1132
|
// make sure this contains a complete CC command that's worth splitting
|
|
1132
1133
|
validatePayload(decryptedCCBytes.length >= 2);
|
|
1133
1134
|
// and deserialize the CC
|
|
1135
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1134
1136
|
encapsulated = CommandClass.parse(decryptedCCBytes, ctx);
|
|
1135
1137
|
}
|
|
1136
1138
|
const ret = new Security2CCMessageEncapsulation({
|
|
@@ -1148,6 +1150,232 @@ let Security2CCMessageEncapsulation = (() => {
|
|
|
1148
1150
|
ret.plaintext = decryptedCCBytes;
|
|
1149
1151
|
return ret;
|
|
1150
1152
|
}
|
|
1153
|
+
static async fromAsync(raw, ctx) {
|
|
1154
|
+
const securityManager = assertSecurityRX(ctx);
|
|
1155
|
+
validatePayload(raw.payload.length >= 2);
|
|
1156
|
+
// Check the sequence number to avoid duplicates
|
|
1157
|
+
const sequenceNumber = raw.payload[0];
|
|
1158
|
+
// Ensure the node has a security class
|
|
1159
|
+
validatePayload.withReason("No security class granted")(ctx.getHighestSecurityClass(ctx.sourceNodeId) !== SecurityClass.None);
|
|
1160
|
+
const hasExtensions = !!(raw.payload[1] & 0b1);
|
|
1161
|
+
const hasEncryptedExtensions = !!(raw.payload[1] & 0b10);
|
|
1162
|
+
let offset = 2;
|
|
1163
|
+
const extensions = [];
|
|
1164
|
+
let mustDiscardCommand = false;
|
|
1165
|
+
const parseExtensions = (buffer, wasEncrypted) => {
|
|
1166
|
+
while (true) {
|
|
1167
|
+
if (buffer.length < offset + 2) {
|
|
1168
|
+
// An S2 extension was expected, but the buffer is too short
|
|
1169
|
+
mustDiscardCommand = true;
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
// The length field could be too large, which would cause part of the actual ciphertext
|
|
1173
|
+
// to be ignored. Try to avoid this for known extensions by checking the actual and expected length.
|
|
1174
|
+
const { actual: actualLength, expected: expectedLength } = Security2Extension
|
|
1175
|
+
.getExtensionLength(buffer.subarray(offset));
|
|
1176
|
+
// Parse the extension using the expected length if possible
|
|
1177
|
+
const extensionLength = expectedLength ?? actualLength;
|
|
1178
|
+
if (extensionLength < 2) {
|
|
1179
|
+
// An S2 extension was expected, but the length is too short
|
|
1180
|
+
mustDiscardCommand = true;
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
else if (extensionLength
|
|
1184
|
+
> buffer.length
|
|
1185
|
+
- offset
|
|
1186
|
+
- (wasEncrypted
|
|
1187
|
+
? 0
|
|
1188
|
+
: SECURITY_S2_AUTH_TAG_LENGTH)) {
|
|
1189
|
+
// The supposed length is longer than the space the extensions may occupy
|
|
1190
|
+
mustDiscardCommand = true;
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
const extensionData = buffer.subarray(offset, offset + extensionLength);
|
|
1194
|
+
offset += extensionLength;
|
|
1195
|
+
const ext = Security2Extension.parse(extensionData);
|
|
1196
|
+
switch (validateS2Extension(ext, wasEncrypted)) {
|
|
1197
|
+
case ValidateS2ExtensionResult.OK:
|
|
1198
|
+
if (expectedLength != undefined
|
|
1199
|
+
&& actualLength !== expectedLength) {
|
|
1200
|
+
// The extension length field does not match, ignore the extension
|
|
1201
|
+
}
|
|
1202
|
+
else {
|
|
1203
|
+
extensions.push(ext);
|
|
1204
|
+
}
|
|
1205
|
+
break;
|
|
1206
|
+
case ValidateS2ExtensionResult.DiscardExtension:
|
|
1207
|
+
// Do nothing
|
|
1208
|
+
break;
|
|
1209
|
+
case ValidateS2ExtensionResult.DiscardCommand:
|
|
1210
|
+
mustDiscardCommand = true;
|
|
1211
|
+
break;
|
|
1212
|
+
}
|
|
1213
|
+
// Check if that was the last extension
|
|
1214
|
+
if (!ext.moreToFollow)
|
|
1215
|
+
break;
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
if (hasExtensions)
|
|
1219
|
+
parseExtensions(raw.payload, false);
|
|
1220
|
+
const mcctx = (() => {
|
|
1221
|
+
const multicastGroupId = getMulticastGroupId(extensions);
|
|
1222
|
+
if (ctx.frameType === "multicast" || ctx.frameType === "broadcast") {
|
|
1223
|
+
if (multicastGroupId == undefined) {
|
|
1224
|
+
validatePayload.fail("Multicast frames without MGRP extension");
|
|
1225
|
+
}
|
|
1226
|
+
return {
|
|
1227
|
+
isMulticast: true,
|
|
1228
|
+
groupId: multicastGroupId,
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
else {
|
|
1232
|
+
return { isMulticast: false, groupId: multicastGroupId };
|
|
1233
|
+
}
|
|
1234
|
+
})();
|
|
1235
|
+
// If a command is to be discarded before decryption,
|
|
1236
|
+
// we still need to increment the SPAN or MPAN state
|
|
1237
|
+
if (mustDiscardCommand) {
|
|
1238
|
+
if (mcctx.isMulticast) {
|
|
1239
|
+
securityManager.nextPeerMPAN(ctx.sourceNodeId, mcctx.groupId);
|
|
1240
|
+
}
|
|
1241
|
+
else {
|
|
1242
|
+
securityManager.nextNonce(ctx.sourceNodeId);
|
|
1243
|
+
}
|
|
1244
|
+
validatePayload.fail("Invalid S2 extension");
|
|
1245
|
+
}
|
|
1246
|
+
let prevSequenceNumber;
|
|
1247
|
+
let mpanState;
|
|
1248
|
+
if (mcctx.isMulticast) {
|
|
1249
|
+
mpanState = securityManager.getPeerMPAN(ctx.sourceNodeId, mcctx.groupId);
|
|
1250
|
+
}
|
|
1251
|
+
else {
|
|
1252
|
+
// Don't accept duplicate Singlecast commands
|
|
1253
|
+
prevSequenceNumber = validateSequenceNumber(securityManager, ctx.sourceNodeId, sequenceNumber);
|
|
1254
|
+
// When a node receives a singlecast message after a multicast group was marked out of sync,
|
|
1255
|
+
// it must forget about the group.
|
|
1256
|
+
if (mcctx.groupId == undefined) {
|
|
1257
|
+
securityManager.resetOutOfSyncMPANs(ctx.sourceNodeId);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
const unencryptedPayload = raw.payload.subarray(0, offset);
|
|
1261
|
+
const ciphertext = raw.payload.subarray(offset, -SECURITY_S2_AUTH_TAG_LENGTH);
|
|
1262
|
+
const authTag = raw.payload.subarray(-SECURITY_S2_AUTH_TAG_LENGTH);
|
|
1263
|
+
const messageLength = 2 /* CommandClass.computeEncapsulationOverhead() */
|
|
1264
|
+
+ raw.payload.length;
|
|
1265
|
+
const authData = getAuthenticationData(ctx.sourceNodeId, getDestinationIDRX(ctx, extensions), ctx.homeId, messageLength, unencryptedPayload);
|
|
1266
|
+
let decrypt;
|
|
1267
|
+
if (mcctx.isMulticast) {
|
|
1268
|
+
// For incoming multicast commands, make sure we have an MPAN
|
|
1269
|
+
if (mpanState?.type !== MPANState.MPAN) {
|
|
1270
|
+
// If we don't, mark the MPAN as out of sync, so we can respond accordingly on the singlecast followup
|
|
1271
|
+
securityManager.storePeerMPAN(ctx.sourceNodeId, mcctx.groupId, { type: MPANState.OutOfSync });
|
|
1272
|
+
failNoMPAN();
|
|
1273
|
+
}
|
|
1274
|
+
decrypt = () => decryptMulticast(ctx.sourceNodeId, securityManager, mcctx.groupId, ciphertext, authData, authTag);
|
|
1275
|
+
}
|
|
1276
|
+
else {
|
|
1277
|
+
// Decrypt payload and verify integrity
|
|
1278
|
+
const spanState = securityManager.getSPANState(ctx.sourceNodeId);
|
|
1279
|
+
// If we are not able to establish an SPAN yet, fail the decryption
|
|
1280
|
+
if (spanState.type === SPANState.None) {
|
|
1281
|
+
failNoSPAN();
|
|
1282
|
+
}
|
|
1283
|
+
else if (spanState.type === SPANState.RemoteEI) {
|
|
1284
|
+
// TODO: The specs are not clear how to handle this case
|
|
1285
|
+
// For now, do the same as if we didn't have any EI
|
|
1286
|
+
failNoSPAN();
|
|
1287
|
+
}
|
|
1288
|
+
decrypt = () => decryptSinglecast(ctx, securityManager, ctx.sourceNodeId, sequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions);
|
|
1289
|
+
}
|
|
1290
|
+
let plaintext;
|
|
1291
|
+
let authOK = false;
|
|
1292
|
+
let key;
|
|
1293
|
+
let iv;
|
|
1294
|
+
let decryptionSecurityClass;
|
|
1295
|
+
// If the Receiver is unable to authenticate the singlecast message with the current SPAN,
|
|
1296
|
+
// the Receiver SHOULD try decrypting the message with one or more of the following SPAN values,
|
|
1297
|
+
// stopping when decryption is successful or the maximum number of iterations is reached.
|
|
1298
|
+
// If the Receiver is unable to decrypt the S2 MC frame with the current MPAN, the Receiver MAY try
|
|
1299
|
+
// decrypting the frame with one or more of the subsequent MPAN values, stopping when decryption is
|
|
1300
|
+
// successful or the maximum number of iterations is reached.
|
|
1301
|
+
const decryptAttempts = mcctx.isMulticast
|
|
1302
|
+
? MAX_DECRYPT_ATTEMPTS_MULTICAST
|
|
1303
|
+
: mcctx.groupId != undefined
|
|
1304
|
+
? MAX_DECRYPT_ATTEMPTS_SC_FOLLOWUP
|
|
1305
|
+
: MAX_DECRYPT_ATTEMPTS_SINGLECAST;
|
|
1306
|
+
for (let i = 0; i < decryptAttempts; i++) {
|
|
1307
|
+
({
|
|
1308
|
+
plaintext,
|
|
1309
|
+
authOK,
|
|
1310
|
+
key,
|
|
1311
|
+
iv,
|
|
1312
|
+
securityClass: decryptionSecurityClass,
|
|
1313
|
+
} = decrypt());
|
|
1314
|
+
if (!!authOK && !!plaintext)
|
|
1315
|
+
break;
|
|
1316
|
+
// No need to try further SPANs if we just got the sender's EI
|
|
1317
|
+
if (!!getSenderEI(extensions))
|
|
1318
|
+
break;
|
|
1319
|
+
}
|
|
1320
|
+
// If authentication fails, do so with an error code that instructs the
|
|
1321
|
+
// applHost to tell the node we have no nonce
|
|
1322
|
+
if (!authOK || !plaintext) {
|
|
1323
|
+
if (mcctx.isMulticast) {
|
|
1324
|
+
// Mark the MPAN as out of sync
|
|
1325
|
+
securityManager.storePeerMPAN(ctx.sourceNodeId, mcctx.groupId, { type: MPANState.OutOfSync });
|
|
1326
|
+
validatePayload.fail(ZWaveErrorCodes.Security2CC_CannotDecodeMulticast);
|
|
1327
|
+
}
|
|
1328
|
+
else {
|
|
1329
|
+
validatePayload.fail(ZWaveErrorCodes.Security2CC_CannotDecode);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
else if (!mcctx.isMulticast && mcctx.groupId != undefined) {
|
|
1333
|
+
// After reception of a singlecast followup, the MPAN state must be increased
|
|
1334
|
+
securityManager.tryIncrementPeerMPAN(ctx.sourceNodeId, mcctx.groupId);
|
|
1335
|
+
}
|
|
1336
|
+
// Remember which security class was used to decrypt this message, so we can discard it later
|
|
1337
|
+
const securityClass = decryptionSecurityClass;
|
|
1338
|
+
offset = 0;
|
|
1339
|
+
if (hasEncryptedExtensions)
|
|
1340
|
+
parseExtensions(plaintext, true);
|
|
1341
|
+
// Before we can continue, check if the command must be discarded
|
|
1342
|
+
if (mustDiscardCommand) {
|
|
1343
|
+
validatePayload.fail("Invalid extension");
|
|
1344
|
+
}
|
|
1345
|
+
// If the MPAN extension was received, store the MPAN
|
|
1346
|
+
if (!mcctx.isMulticast) {
|
|
1347
|
+
const mpanExtension = extensions.find((e) => e instanceof MPANExtension);
|
|
1348
|
+
if (mpanExtension) {
|
|
1349
|
+
securityManager.storePeerMPAN(ctx.sourceNodeId, mpanExtension.groupId, {
|
|
1350
|
+
type: MPANState.MPAN,
|
|
1351
|
+
currentMPAN: mpanExtension.innerMPANState,
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
// Not every S2 message includes an encapsulated CC
|
|
1356
|
+
const decryptedCCBytes = plaintext.subarray(offset);
|
|
1357
|
+
let encapsulated;
|
|
1358
|
+
if (decryptedCCBytes.length > 0) {
|
|
1359
|
+
// make sure this contains a complete CC command that's worth splitting
|
|
1360
|
+
validatePayload(decryptedCCBytes.length >= 2);
|
|
1361
|
+
// and deserialize the CC
|
|
1362
|
+
encapsulated = await CommandClass.parseAsync(decryptedCCBytes, ctx);
|
|
1363
|
+
}
|
|
1364
|
+
const ret = new Security2CCMessageEncapsulation({
|
|
1365
|
+
nodeId: ctx.sourceNodeId,
|
|
1366
|
+
sequenceNumber,
|
|
1367
|
+
securityClass,
|
|
1368
|
+
extensions,
|
|
1369
|
+
encapsulated,
|
|
1370
|
+
});
|
|
1371
|
+
// Remember for debugging purposes
|
|
1372
|
+
ret.key = key;
|
|
1373
|
+
ret.iv = iv;
|
|
1374
|
+
ret.authData = authData;
|
|
1375
|
+
ret.authTag = authTag;
|
|
1376
|
+
ret.plaintext = decryptedCCBytes;
|
|
1377
|
+
return ret;
|
|
1378
|
+
}
|
|
1151
1379
|
securityClass;
|
|
1152
1380
|
// Only used for testing/debugging purposes
|
|
1153
1381
|
key;
|
|
@@ -1227,6 +1455,7 @@ let Security2CCMessageEncapsulation = (() => {
|
|
|
1227
1455
|
}
|
|
1228
1456
|
}
|
|
1229
1457
|
}
|
|
1458
|
+
/** @deprecated Use {@link serializeAsync} instead */
|
|
1230
1459
|
serialize(ctx) {
|
|
1231
1460
|
const securityManager = assertSecurityTX(ctx, this.nodeId);
|
|
1232
1461
|
this.ensureSequenceNumber(securityManager);
|
|
@@ -1242,6 +1471,7 @@ let Security2CCMessageEncapsulation = (() => {
|
|
|
1242
1471
|
]),
|
|
1243
1472
|
...unencryptedExtensions.map((e, index) => e.serialize(index < unencryptedExtensions.length - 1)),
|
|
1244
1473
|
]);
|
|
1474
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1245
1475
|
const serializedCC = this.encapsulated?.serialize(ctx)
|
|
1246
1476
|
?? new Bytes();
|
|
1247
1477
|
const plaintextPayload = Bytes.concat([
|
|
@@ -1285,8 +1515,69 @@ let Security2CCMessageEncapsulation = (() => {
|
|
|
1285
1515
|
ciphertextPayload,
|
|
1286
1516
|
authTag,
|
|
1287
1517
|
]);
|
|
1518
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1288
1519
|
return super.serialize(ctx);
|
|
1289
1520
|
}
|
|
1521
|
+
async serializeAsync(ctx) {
|
|
1522
|
+
const securityManager = assertSecurityTX(ctx, this.nodeId);
|
|
1523
|
+
this.ensureSequenceNumber(securityManager);
|
|
1524
|
+
// Include Sender EI in the command if we only have the receiver's EI
|
|
1525
|
+
this.maybeAddSPANExtension(ctx, securityManager);
|
|
1526
|
+
const unencryptedExtensions = this.extensions.filter((e) => !e.isEncrypted());
|
|
1527
|
+
const encryptedExtensions = this.extensions.filter((e) => e.isEncrypted());
|
|
1528
|
+
const unencryptedPayload = Bytes.concat([
|
|
1529
|
+
Bytes.from([
|
|
1530
|
+
this.sequenceNumber,
|
|
1531
|
+
(encryptedExtensions.length > 0 ? 0b10 : 0)
|
|
1532
|
+
| (unencryptedExtensions.length > 0 ? 1 : 0),
|
|
1533
|
+
]),
|
|
1534
|
+
...unencryptedExtensions.map((e, index) => e.serialize(index < unencryptedExtensions.length - 1)),
|
|
1535
|
+
]);
|
|
1536
|
+
const serializedCC = (await this.encapsulated?.serializeAsync(ctx))
|
|
1537
|
+
?? new Bytes();
|
|
1538
|
+
const plaintextPayload = Bytes.concat([
|
|
1539
|
+
...encryptedExtensions.map((e, index) => e.serialize(index < encryptedExtensions.length - 1)),
|
|
1540
|
+
serializedCC,
|
|
1541
|
+
]);
|
|
1542
|
+
// Generate the authentication data for CCM encryption
|
|
1543
|
+
const destinationTag = getDestinationIDTX.call(this);
|
|
1544
|
+
const messageLength = this.computeEncapsulationOverhead()
|
|
1545
|
+
+ serializedCC.length;
|
|
1546
|
+
const authData = getAuthenticationData(ctx.ownNodeId, destinationTag, ctx.homeId, messageLength, unencryptedPayload);
|
|
1547
|
+
let key;
|
|
1548
|
+
let iv;
|
|
1549
|
+
if (this.isSinglecast()) {
|
|
1550
|
+
// Singlecast:
|
|
1551
|
+
// Generate a nonce for encryption, and remember it to attempt decryption
|
|
1552
|
+
// of potential in-flight messages from the target node.
|
|
1553
|
+
iv = securityManager.nextNonce(this.nodeId, true);
|
|
1554
|
+
const { keyCCM } =
|
|
1555
|
+
// Prefer the overridden security class if it was given
|
|
1556
|
+
this.securityClass != undefined
|
|
1557
|
+
? securityManager.getKeysForSecurityClass(this.securityClass)
|
|
1558
|
+
: securityManager.getKeysForNode(this.nodeId);
|
|
1559
|
+
key = keyCCM;
|
|
1560
|
+
}
|
|
1561
|
+
else {
|
|
1562
|
+
// Multicast:
|
|
1563
|
+
const keyAndIV = securityManager.getMulticastKeyAndIV(destinationTag);
|
|
1564
|
+
key = keyAndIV.key;
|
|
1565
|
+
iv = keyAndIV.iv;
|
|
1566
|
+
}
|
|
1567
|
+
const { ciphertext: ciphertextPayload, authTag } = encryptAES128CCM(key, iv, plaintextPayload, authData, SECURITY_S2_AUTH_TAG_LENGTH);
|
|
1568
|
+
// Remember key and IV for debugging purposes
|
|
1569
|
+
this.key = key;
|
|
1570
|
+
this.iv = iv;
|
|
1571
|
+
this.authData = authData;
|
|
1572
|
+
this.authTag = authTag;
|
|
1573
|
+
this.ciphertext = ciphertextPayload;
|
|
1574
|
+
this.payload = Bytes.concat([
|
|
1575
|
+
unencryptedPayload,
|
|
1576
|
+
ciphertextPayload,
|
|
1577
|
+
authTag,
|
|
1578
|
+
]);
|
|
1579
|
+
return super.serializeAsync(ctx);
|
|
1580
|
+
}
|
|
1290
1581
|
computeEncapsulationOverhead() {
|
|
1291
1582
|
// Security S2 adds:
|
|
1292
1583
|
// * 1 byte sequence number
|
|
@@ -1422,6 +1713,7 @@ let Security2CCNonceReport = (() => {
|
|
|
1422
1713
|
if (this.SOS) {
|
|
1423
1714
|
this.payload = Bytes.concat([this.payload, this.receiverEI]);
|
|
1424
1715
|
}
|
|
1716
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1425
1717
|
return super.serialize(ctx);
|
|
1426
1718
|
}
|
|
1427
1719
|
toLogEntry(ctx) {
|
|
@@ -1484,6 +1776,7 @@ let Security2CCNonceGet = (() => {
|
|
|
1484
1776
|
const securityManager = assertSecurityTX(ctx, this.nodeId);
|
|
1485
1777
|
this.ensureSequenceNumber(securityManager);
|
|
1486
1778
|
this.payload = Bytes.from([this.sequenceNumber]);
|
|
1779
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1487
1780
|
return super.serialize(ctx);
|
|
1488
1781
|
}
|
|
1489
1782
|
toLogEntry(ctx) {
|
|
@@ -1560,6 +1853,7 @@ let Security2CCKEXReport = (() => {
|
|
|
1560
1853
|
encodeBitMask(this.supportedECDHProfiles, 7, ECDHProfiles.Curve25519),
|
|
1561
1854
|
encodeBitMask(this.requestedKeys, SecurityClass.S0_Legacy, SecurityClass.S2_Unauthenticated),
|
|
1562
1855
|
]);
|
|
1856
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1563
1857
|
return super.serialize(ctx);
|
|
1564
1858
|
}
|
|
1565
1859
|
toLogEntry(ctx) {
|
|
@@ -1687,6 +1981,7 @@ let Security2CCKEXSet = (() => {
|
|
|
1687
1981
|
encodeBitMask([this.selectedECDHProfile], 7, ECDHProfiles.Curve25519),
|
|
1688
1982
|
encodeBitMask(this.grantedKeys, SecurityClass.S0_Legacy, SecurityClass.S2_Unauthenticated),
|
|
1689
1983
|
]);
|
|
1984
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1690
1985
|
return super.serialize(ctx);
|
|
1691
1986
|
}
|
|
1692
1987
|
toLogEntry(ctx) {
|
|
@@ -1737,6 +2032,7 @@ let Security2CCKEXFail = (() => {
|
|
|
1737
2032
|
failType;
|
|
1738
2033
|
serialize(ctx) {
|
|
1739
2034
|
this.payload = Bytes.from([this.failType]);
|
|
2035
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1740
2036
|
return super.serialize(ctx);
|
|
1741
2037
|
}
|
|
1742
2038
|
toLogEntry(ctx) {
|
|
@@ -1786,6 +2082,7 @@ let Security2CCPublicKeyReport = (() => {
|
|
|
1786
2082
|
Bytes.from([this.includingNode ? 1 : 0]),
|
|
1787
2083
|
this.publicKey,
|
|
1788
2084
|
]);
|
|
2085
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1789
2086
|
return super.serialize(ctx);
|
|
1790
2087
|
}
|
|
1791
2088
|
toLogEntry(ctx) {
|
|
@@ -1838,6 +2135,7 @@ let Security2CCNetworkKeyReport = (() => {
|
|
|
1838
2135
|
securityClassToBitMask(this.grantedKey),
|
|
1839
2136
|
this.networkKey,
|
|
1840
2137
|
]);
|
|
2138
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1841
2139
|
return super.serialize(ctx);
|
|
1842
2140
|
}
|
|
1843
2141
|
toLogEntry(ctx) {
|
|
@@ -1884,6 +2182,7 @@ let Security2CCNetworkKeyGet = (() => {
|
|
|
1884
2182
|
requestedKey;
|
|
1885
2183
|
serialize(ctx) {
|
|
1886
2184
|
this.payload = securityClassToBitMask(this.requestedKey);
|
|
2185
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1887
2186
|
return super.serialize(ctx);
|
|
1888
2187
|
}
|
|
1889
2188
|
toLogEntry(ctx) {
|
|
@@ -1953,6 +2252,7 @@ let Security2CCTransferEnd = (() => {
|
|
|
1953
2252
|
this.payload = Bytes.from([
|
|
1954
2253
|
(this.keyVerified ? 0b10 : 0) + (this.keyRequestComplete ? 0b1 : 0),
|
|
1955
2254
|
]);
|
|
2255
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1956
2256
|
return super.serialize(ctx);
|
|
1957
2257
|
}
|
|
1958
2258
|
toLogEntry(ctx) {
|
|
@@ -2002,6 +2302,7 @@ let Security2CCCommandsSupportedReport = (() => {
|
|
|
2002
2302
|
supportedCCs;
|
|
2003
2303
|
serialize(ctx) {
|
|
2004
2304
|
this.payload = encodeCCList(this.supportedCCs, []);
|
|
2305
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
2005
2306
|
return super.serialize(ctx);
|
|
2006
2307
|
}
|
|
2007
2308
|
toLogEntry(ctx) {
|