@zwave-js/cc 14.1.0 → 14.3.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.
Files changed (292) hide show
  1. package/build/cjs/cc/AlarmSensorCC.js.map +2 -2
  2. package/build/cjs/cc/AssociationCC.d.ts +2 -0
  3. package/build/cjs/cc/AssociationCC.js +5 -0
  4. package/build/cjs/cc/AssociationCC.js.map +2 -2
  5. package/build/cjs/cc/AssociationGroupInfoCC.js.map +2 -2
  6. package/build/cjs/cc/BarrierOperatorCC.js.map +2 -2
  7. package/build/cjs/cc/BasicCC.js.map +2 -2
  8. package/build/cjs/cc/BatteryCC.js.map +2 -2
  9. package/build/cjs/cc/BinarySensorCC.js.map +2 -2
  10. package/build/cjs/cc/BinarySwitchCC.js.map +2 -2
  11. package/build/cjs/cc/CRC16CC.d.ts +4 -0
  12. package/build/cjs/cc/CRC16CC.js +23 -0
  13. package/build/cjs/cc/CRC16CC.js.map +2 -2
  14. package/build/cjs/cc/CentralSceneCC.js.map +2 -2
  15. package/build/cjs/cc/ClimateControlScheduleCC.js.map +2 -2
  16. package/build/cjs/cc/ClockCC.js.map +2 -2
  17. package/build/cjs/cc/ColorSwitchCC.js.map +2 -2
  18. package/build/cjs/cc/ConfigurationCC.d.ts +4 -0
  19. package/build/cjs/cc/ConfigurationCC.js +10 -0
  20. package/build/cjs/cc/ConfigurationCC.js.map +2 -2
  21. package/build/cjs/cc/DoorLockCC.js.map +2 -2
  22. package/build/cjs/cc/DoorLockLoggingCC.js.map +2 -2
  23. package/build/cjs/cc/EnergyProductionCC.js.map +2 -2
  24. package/build/cjs/cc/EntryControlCC.js.map +2 -2
  25. package/build/cjs/cc/FirmwareUpdateMetaDataCC.js.map +2 -2
  26. package/build/cjs/cc/HumidityControlModeCC.js.map +2 -2
  27. package/build/cjs/cc/HumidityControlSetpointCC.js.map +2 -2
  28. package/build/cjs/cc/InclusionControllerCC.js.map +2 -2
  29. package/build/cjs/cc/IndicatorCC.js.map +2 -2
  30. package/build/cjs/cc/IrrigationCC.js.map +2 -2
  31. package/build/cjs/cc/LanguageCC.js.map +2 -2
  32. package/build/cjs/cc/LockCC.js.map +2 -2
  33. package/build/cjs/cc/ManufacturerProprietaryCC.js.map +2 -2
  34. package/build/cjs/cc/ManufacturerSpecificCC.js.map +2 -2
  35. package/build/cjs/cc/MeterCC.js.map +2 -2
  36. package/build/cjs/cc/MultiChannelAssociationCC.d.ts +2 -0
  37. package/build/cjs/cc/MultiChannelAssociationCC.js +6 -0
  38. package/build/cjs/cc/MultiChannelAssociationCC.js.map +2 -2
  39. package/build/cjs/cc/MultiChannelCC.d.ts +10 -0
  40. package/build/cjs/cc/MultiChannelCC.js +65 -0
  41. package/build/cjs/cc/MultiChannelCC.js.map +2 -2
  42. package/build/cjs/cc/MultiCommandCC.d.ts +4 -0
  43. package/build/cjs/cc/MultiCommandCC.js +34 -1
  44. package/build/cjs/cc/MultiCommandCC.js.map +2 -2
  45. package/build/cjs/cc/MultilevelSensorCC.js.map +2 -2
  46. package/build/cjs/cc/MultilevelSwitchCC.js.map +2 -2
  47. package/build/cjs/cc/NodeNamingCC.js.map +2 -2
  48. package/build/cjs/cc/NotificationCC.js.map +2 -2
  49. package/build/cjs/cc/PowerlevelCC.js.map +2 -2
  50. package/build/cjs/cc/ProtectionCC.js.map +2 -2
  51. package/build/cjs/cc/SceneActivationCC.js.map +2 -2
  52. package/build/cjs/cc/SceneActuatorConfigurationCC.js.map +2 -2
  53. package/build/cjs/cc/SceneControllerConfigurationCC.js.map +2 -2
  54. package/build/cjs/cc/ScheduleEntryLockCC.js.map +2 -2
  55. package/build/cjs/cc/Security2CC.d.ts +7 -1
  56. package/build/cjs/cc/Security2CC.js +383 -52
  57. package/build/cjs/cc/Security2CC.js.map +2 -2
  58. package/build/cjs/cc/SecurityCC.d.ts +7 -0
  59. package/build/cjs/cc/SecurityCC.js +101 -6
  60. package/build/cjs/cc/SecurityCC.js.map +2 -2
  61. package/build/cjs/cc/SoundSwitchCC.js.map +2 -2
  62. package/build/cjs/cc/SupervisionCC.d.ts +4 -0
  63. package/build/cjs/cc/SupervisionCC.js +25 -0
  64. package/build/cjs/cc/SupervisionCC.js.map +2 -2
  65. package/build/cjs/cc/ThermostatFanModeCC.js.map +2 -2
  66. package/build/cjs/cc/ThermostatModeCC.js.map +2 -2
  67. package/build/cjs/cc/ThermostatSetbackCC.js.map +2 -2
  68. package/build/cjs/cc/ThermostatSetpointCC.js.map +2 -2
  69. package/build/cjs/cc/TimeCC.js.map +2 -2
  70. package/build/cjs/cc/TimeParametersCC.js.map +2 -2
  71. package/build/cjs/cc/TransportServiceCC.d.ts +5 -0
  72. package/build/cjs/cc/TransportServiceCC.js +13 -0
  73. package/build/cjs/cc/TransportServiceCC.js.map +2 -2
  74. package/build/cjs/cc/UserCodeCC.js.map +2 -2
  75. package/build/cjs/cc/VersionCC.js.map +2 -2
  76. package/build/cjs/cc/WakeUpCC.js.map +2 -2
  77. package/build/cjs/cc/WindowCoveringCC.js.map +2 -2
  78. package/build/cjs/cc/ZWavePlusCC.js.map +2 -2
  79. package/build/cjs/cc/ZWaveProtocolCC.js.map +2 -2
  80. package/build/cjs/cc/manufacturerProprietary/Decorators.js +6 -5
  81. package/build/cjs/cc/manufacturerProprietary/Decorators.js.map +2 -2
  82. package/build/cjs/cc/manufacturerProprietary/FibaroCC.js.map +2 -2
  83. package/build/cjs/lib/CommandClass.d.ts +15 -1
  84. package/build/cjs/lib/CommandClass.js +56 -1
  85. package/build/cjs/lib/CommandClass.js.map +2 -2
  86. package/build/cjs/lib/CommandClassDecorators.js +7 -6
  87. package/build/cjs/lib/CommandClassDecorators.js.map +2 -2
  88. package/build/cjs/lib/Security2/Extension.js +2 -2
  89. package/build/cjs/lib/Security2/Extension.js.map +2 -2
  90. package/build/esm/cc/AlarmSensorCC.d.ts.map +1 -1
  91. package/build/esm/cc/AlarmSensorCC.js +1 -0
  92. package/build/esm/cc/AlarmSensorCC.js.map +1 -1
  93. package/build/esm/cc/AssociationCC.d.ts +2 -0
  94. package/build/esm/cc/AssociationCC.d.ts.map +1 -1
  95. package/build/esm/cc/AssociationCC.js +14 -0
  96. package/build/esm/cc/AssociationCC.js.map +1 -1
  97. package/build/esm/cc/AssociationGroupInfoCC.d.ts.map +1 -1
  98. package/build/esm/cc/AssociationGroupInfoCC.js +6 -0
  99. package/build/esm/cc/AssociationGroupInfoCC.js.map +1 -1
  100. package/build/esm/cc/BarrierOperatorCC.d.ts.map +1 -1
  101. package/build/esm/cc/BarrierOperatorCC.js +3 -0
  102. package/build/esm/cc/BarrierOperatorCC.js.map +1 -1
  103. package/build/esm/cc/BasicCC.d.ts.map +1 -1
  104. package/build/esm/cc/BasicCC.js +2 -0
  105. package/build/esm/cc/BasicCC.js.map +1 -1
  106. package/build/esm/cc/BatteryCC.d.ts.map +1 -1
  107. package/build/esm/cc/BatteryCC.js +1 -0
  108. package/build/esm/cc/BatteryCC.js.map +1 -1
  109. package/build/esm/cc/BinarySensorCC.d.ts.map +1 -1
  110. package/build/esm/cc/BinarySensorCC.js +3 -0
  111. package/build/esm/cc/BinarySensorCC.js.map +1 -1
  112. package/build/esm/cc/BinarySwitchCC.d.ts.map +1 -1
  113. package/build/esm/cc/BinarySwitchCC.js +2 -0
  114. package/build/esm/cc/BinarySwitchCC.js.map +1 -1
  115. package/build/esm/cc/CRC16CC.d.ts +4 -0
  116. package/build/esm/cc/CRC16CC.d.ts.map +1 -1
  117. package/build/esm/cc/CRC16CC.js +30 -0
  118. package/build/esm/cc/CRC16CC.js.map +1 -1
  119. package/build/esm/cc/CentralSceneCC.d.ts.map +1 -1
  120. package/build/esm/cc/CentralSceneCC.js +1 -0
  121. package/build/esm/cc/CentralSceneCC.js.map +1 -1
  122. package/build/esm/cc/ClimateControlScheduleCC.d.ts.map +1 -1
  123. package/build/esm/cc/ClimateControlScheduleCC.js +3 -0
  124. package/build/esm/cc/ClimateControlScheduleCC.js.map +1 -1
  125. package/build/esm/cc/ClockCC.d.ts.map +1 -1
  126. package/build/esm/cc/ClockCC.js +1 -0
  127. package/build/esm/cc/ClockCC.js.map +1 -1
  128. package/build/esm/cc/ColorSwitchCC.d.ts.map +1 -1
  129. package/build/esm/cc/ColorSwitchCC.js +6 -0
  130. package/build/esm/cc/ColorSwitchCC.js.map +1 -1
  131. package/build/esm/cc/ConfigurationCC.d.ts +4 -0
  132. package/build/esm/cc/ConfigurationCC.d.ts.map +1 -1
  133. package/build/esm/cc/ConfigurationCC.js +27 -0
  134. package/build/esm/cc/ConfigurationCC.js.map +1 -1
  135. package/build/esm/cc/DoorLockCC.d.ts.map +1 -1
  136. package/build/esm/cc/DoorLockCC.js +2 -0
  137. package/build/esm/cc/DoorLockCC.js.map +1 -1
  138. package/build/esm/cc/DoorLockLoggingCC.d.ts.map +1 -1
  139. package/build/esm/cc/DoorLockLoggingCC.js +1 -0
  140. package/build/esm/cc/DoorLockLoggingCC.js.map +1 -1
  141. package/build/esm/cc/EnergyProductionCC.d.ts.map +1 -1
  142. package/build/esm/cc/EnergyProductionCC.js +2 -0
  143. package/build/esm/cc/EnergyProductionCC.js.map +1 -1
  144. package/build/esm/cc/EntryControlCC.d.ts.map +1 -1
  145. package/build/esm/cc/EntryControlCC.js +1 -0
  146. package/build/esm/cc/EntryControlCC.js.map +1 -1
  147. package/build/esm/cc/FirmwareUpdateMetaDataCC.d.ts.map +1 -1
  148. package/build/esm/cc/FirmwareUpdateMetaDataCC.js +5 -0
  149. package/build/esm/cc/FirmwareUpdateMetaDataCC.js.map +1 -1
  150. package/build/esm/cc/HumidityControlModeCC.d.ts.map +1 -1
  151. package/build/esm/cc/HumidityControlModeCC.js +1 -0
  152. package/build/esm/cc/HumidityControlModeCC.js.map +1 -1
  153. package/build/esm/cc/HumidityControlSetpointCC.d.ts.map +1 -1
  154. package/build/esm/cc/HumidityControlSetpointCC.js +4 -0
  155. package/build/esm/cc/HumidityControlSetpointCC.js.map +1 -1
  156. package/build/esm/cc/InclusionControllerCC.d.ts.map +1 -1
  157. package/build/esm/cc/InclusionControllerCC.js +2 -0
  158. package/build/esm/cc/InclusionControllerCC.js.map +1 -1
  159. package/build/esm/cc/IndicatorCC.d.ts.map +1 -1
  160. package/build/esm/cc/IndicatorCC.js +7 -0
  161. package/build/esm/cc/IndicatorCC.js.map +1 -1
  162. package/build/esm/cc/IrrigationCC.d.ts.map +1 -1
  163. package/build/esm/cc/IrrigationCC.js +9 -0
  164. package/build/esm/cc/IrrigationCC.js.map +1 -1
  165. package/build/esm/cc/LanguageCC.d.ts.map +1 -1
  166. package/build/esm/cc/LanguageCC.js +1 -0
  167. package/build/esm/cc/LanguageCC.js.map +1 -1
  168. package/build/esm/cc/LockCC.d.ts.map +1 -1
  169. package/build/esm/cc/LockCC.js +1 -0
  170. package/build/esm/cc/LockCC.js.map +1 -1
  171. package/build/esm/cc/ManufacturerProprietaryCC.d.ts.map +1 -1
  172. package/build/esm/cc/ManufacturerProprietaryCC.js +2 -0
  173. package/build/esm/cc/ManufacturerProprietaryCC.js.map +1 -1
  174. package/build/esm/cc/ManufacturerSpecificCC.d.ts.map +1 -1
  175. package/build/esm/cc/ManufacturerSpecificCC.js +2 -0
  176. package/build/esm/cc/ManufacturerSpecificCC.js.map +1 -1
  177. package/build/esm/cc/MeterCC.d.ts.map +1 -1
  178. package/build/esm/cc/MeterCC.js +4 -0
  179. package/build/esm/cc/MeterCC.js.map +1 -1
  180. package/build/esm/cc/MultiChannelAssociationCC.d.ts +2 -0
  181. package/build/esm/cc/MultiChannelAssociationCC.d.ts.map +1 -1
  182. package/build/esm/cc/MultiChannelAssociationCC.js +17 -0
  183. package/build/esm/cc/MultiChannelAssociationCC.js.map +1 -1
  184. package/build/esm/cc/MultiChannelCC.d.ts +10 -0
  185. package/build/esm/cc/MultiChannelCC.d.ts.map +1 -1
  186. package/build/esm/cc/MultiChannelCC.js +94 -0
  187. package/build/esm/cc/MultiChannelCC.js.map +1 -1
  188. package/build/esm/cc/MultiCommandCC.d.ts +4 -0
  189. package/build/esm/cc/MultiCommandCC.d.ts.map +1 -1
  190. package/build/esm/cc/MultiCommandCC.js +35 -1
  191. package/build/esm/cc/MultiCommandCC.js.map +1 -1
  192. package/build/esm/cc/MultilevelSensorCC.d.ts.map +1 -1
  193. package/build/esm/cc/MultilevelSensorCC.js +5 -0
  194. package/build/esm/cc/MultilevelSensorCC.js.map +1 -1
  195. package/build/esm/cc/MultilevelSwitchCC.d.ts.map +1 -1
  196. package/build/esm/cc/MultilevelSwitchCC.js +4 -0
  197. package/build/esm/cc/MultilevelSwitchCC.js.map +1 -1
  198. package/build/esm/cc/NodeNamingCC.d.ts.map +1 -1
  199. package/build/esm/cc/NodeNamingCC.js +2 -0
  200. package/build/esm/cc/NodeNamingCC.js.map +1 -1
  201. package/build/esm/cc/NotificationCC.d.ts.map +1 -1
  202. package/build/esm/cc/NotificationCC.js +9 -0
  203. package/build/esm/cc/NotificationCC.js.map +1 -1
  204. package/build/esm/cc/PowerlevelCC.d.ts.map +1 -1
  205. package/build/esm/cc/PowerlevelCC.js +4 -0
  206. package/build/esm/cc/PowerlevelCC.js.map +1 -1
  207. package/build/esm/cc/ProtectionCC.d.ts.map +1 -1
  208. package/build/esm/cc/ProtectionCC.js +3 -0
  209. package/build/esm/cc/ProtectionCC.js.map +1 -1
  210. package/build/esm/cc/SceneActivationCC.d.ts.map +1 -1
  211. package/build/esm/cc/SceneActivationCC.js +1 -0
  212. package/build/esm/cc/SceneActivationCC.js.map +1 -1
  213. package/build/esm/cc/SceneActuatorConfigurationCC.d.ts.map +1 -1
  214. package/build/esm/cc/SceneActuatorConfigurationCC.js +2 -0
  215. package/build/esm/cc/SceneActuatorConfigurationCC.js.map +1 -1
  216. package/build/esm/cc/SceneControllerConfigurationCC.d.ts.map +1 -1
  217. package/build/esm/cc/SceneControllerConfigurationCC.js +2 -0
  218. package/build/esm/cc/SceneControllerConfigurationCC.js.map +1 -1
  219. package/build/esm/cc/ScheduleEntryLockCC.d.ts.map +1 -1
  220. package/build/esm/cc/ScheduleEntryLockCC.js +14 -0
  221. package/build/esm/cc/ScheduleEntryLockCC.js.map +1 -1
  222. package/build/esm/cc/Security2CC.d.ts +7 -1
  223. package/build/esm/cc/Security2CC.d.ts.map +1 -1
  224. package/build/esm/cc/Security2CC.js +534 -68
  225. package/build/esm/cc/Security2CC.js.map +1 -1
  226. package/build/esm/cc/SecurityCC.d.ts +7 -0
  227. package/build/esm/cc/SecurityCC.d.ts.map +1 -1
  228. package/build/esm/cc/SecurityCC.js +133 -7
  229. package/build/esm/cc/SecurityCC.js.map +1 -1
  230. package/build/esm/cc/SoundSwitchCC.d.ts.map +1 -1
  231. package/build/esm/cc/SoundSwitchCC.js +7 -0
  232. package/build/esm/cc/SoundSwitchCC.js.map +1 -1
  233. package/build/esm/cc/SupervisionCC.d.ts +4 -0
  234. package/build/esm/cc/SupervisionCC.d.ts.map +1 -1
  235. package/build/esm/cc/SupervisionCC.js +30 -0
  236. package/build/esm/cc/SupervisionCC.js.map +1 -1
  237. package/build/esm/cc/ThermostatFanModeCC.d.ts.map +1 -1
  238. package/build/esm/cc/ThermostatFanModeCC.js +1 -0
  239. package/build/esm/cc/ThermostatFanModeCC.js.map +1 -1
  240. package/build/esm/cc/ThermostatModeCC.d.ts.map +1 -1
  241. package/build/esm/cc/ThermostatModeCC.js +3 -0
  242. package/build/esm/cc/ThermostatModeCC.js.map +1 -1
  243. package/build/esm/cc/ThermostatSetbackCC.d.ts.map +1 -1
  244. package/build/esm/cc/ThermostatSetbackCC.js +2 -0
  245. package/build/esm/cc/ThermostatSetbackCC.js.map +1 -1
  246. package/build/esm/cc/ThermostatSetpointCC.d.ts.map +1 -1
  247. package/build/esm/cc/ThermostatSetpointCC.js +6 -0
  248. package/build/esm/cc/ThermostatSetpointCC.js.map +1 -1
  249. package/build/esm/cc/TimeCC.d.ts.map +1 -1
  250. package/build/esm/cc/TimeCC.js +4 -0
  251. package/build/esm/cc/TimeCC.js.map +1 -1
  252. package/build/esm/cc/TimeParametersCC.d.ts.map +1 -1
  253. package/build/esm/cc/TimeParametersCC.js +1 -0
  254. package/build/esm/cc/TimeParametersCC.js.map +1 -1
  255. package/build/esm/cc/TransportServiceCC.d.ts +5 -0
  256. package/build/esm/cc/TransportServiceCC.d.ts.map +1 -1
  257. package/build/esm/cc/TransportServiceCC.js +24 -0
  258. package/build/esm/cc/TransportServiceCC.js.map +1 -1
  259. package/build/esm/cc/UserCodeCC.d.ts.map +1 -1
  260. package/build/esm/cc/UserCodeCC.js +12 -0
  261. package/build/esm/cc/UserCodeCC.js.map +1 -1
  262. package/build/esm/cc/VersionCC.d.ts.map +1 -1
  263. package/build/esm/cc/VersionCC.js +4 -0
  264. package/build/esm/cc/VersionCC.js.map +1 -1
  265. package/build/esm/cc/WakeUpCC.d.ts.map +1 -1
  266. package/build/esm/cc/WakeUpCC.js +1 -0
  267. package/build/esm/cc/WakeUpCC.js.map +1 -1
  268. package/build/esm/cc/WindowCoveringCC.d.ts.map +1 -1
  269. package/build/esm/cc/WindowCoveringCC.js +5 -0
  270. package/build/esm/cc/WindowCoveringCC.js.map +1 -1
  271. package/build/esm/cc/ZWavePlusCC.d.ts.map +1 -1
  272. package/build/esm/cc/ZWavePlusCC.js +1 -0
  273. package/build/esm/cc/ZWavePlusCC.js.map +1 -1
  274. package/build/esm/cc/ZWaveProtocolCC.d.ts.map +1 -1
  275. package/build/esm/cc/ZWaveProtocolCC.js +27 -0
  276. package/build/esm/cc/ZWaveProtocolCC.js.map +1 -1
  277. package/build/esm/cc/manufacturerProprietary/Decorators.d.ts.map +1 -1
  278. package/build/esm/cc/manufacturerProprietary/Decorators.js +2 -1
  279. package/build/esm/cc/manufacturerProprietary/Decorators.js.map +1 -1
  280. package/build/esm/cc/manufacturerProprietary/FibaroCC.d.ts.map +1 -1
  281. package/build/esm/cc/manufacturerProprietary/FibaroCC.js +3 -0
  282. package/build/esm/cc/manufacturerProprietary/FibaroCC.js.map +1 -1
  283. package/build/esm/lib/CommandClass.d.ts +15 -1
  284. package/build/esm/lib/CommandClass.d.ts.map +1 -1
  285. package/build/esm/lib/CommandClass.js +94 -1
  286. package/build/esm/lib/CommandClass.js.map +1 -1
  287. package/build/esm/lib/CommandClassDecorators.d.ts.map +1 -1
  288. package/build/esm/lib/CommandClassDecorators.js +2 -1
  289. package/build/esm/lib/CommandClassDecorators.js.map +1 -1
  290. package/build/esm/lib/Security2/Extension.js +1 -1
  291. package/build/esm/lib/Security2/Extension.js.map +1 -1
  292. package/package.json +6 -6
@@ -32,7 +32,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { CommandClasses, MPANState, MessagePriority, SECURITY_S2_AUTH_TAG_LENGTH, SPANState, SecurityClass, TransmitOptions, ZWaveError, ZWaveErrorCodes, decryptAES128CCM, encodeBitMask, encryptAES128CCM, getCCName, highResTimestamp, isLongRangeNodeId, isTransmissionError, isZWaveError, parseBitMask, parseCCList, securityClassIsS2, securityClassOrder, validatePayload, } from "@zwave-js/core";
35
+ import { CommandClasses, MPANState, MessagePriority, SPANState, SecurityClass, TransmitOptions, ZWaveError, ZWaveErrorCodes, decryptAES128CCMAsync, decryptAES128CCMSync, encodeBitMask, encryptAES128CCMAsync, encryptAES128CCMSync, getCCName, highResTimestamp, isLongRangeNodeId, isTransmissionError, isZWaveError, parseBitMask, parseCCList, securityClassIsS2, securityClassOrder, validatePayload, } from "@zwave-js/core";
36
36
  import { EncapsulationFlags, NODE_ID_BROADCAST, NODE_ID_BROADCAST_LR, encodeCCList, } from "@zwave-js/core/safe";
37
37
  import { Bytes } from "@zwave-js/shared/safe";
38
38
  import { buffer2hex, getEnumMemberName, pick } from "@zwave-js/shared/safe";
@@ -56,6 +56,7 @@ function bitMaskToSecurityClass(buffer, offset) {
56
56
  validatePayload(keys.length === 1);
57
57
  return keys[0];
58
58
  }
59
+ const SECURITY_S2_AUTH_TAG_LENGTH = 8;
59
60
  function getAuthenticationData(sendingNodeId, destination, homeId, commandLength, unencryptedPayload) {
60
61
  const nodeIdSize = isLongRangeNodeId(sendingNodeId) || isLongRangeNodeId(destination)
61
62
  ? 2
@@ -110,17 +111,20 @@ function assertSecurityTX(ctx, destination) {
110
111
  }
111
112
  return ret;
112
113
  }
113
- function decryptSinglecast(ctx, securityManager, sendingNodeId, curSequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions) {
114
+ /** @deprecated Use {@link decryptSinglecastAsync} instead */
115
+ function decryptSinglecastSync(ctx, securityManager, sendingNodeId, curSequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions) {
114
116
  const decryptWithNonce = (nonce) => {
115
117
  const { keyCCM: key } = securityManager.getKeysForNode(sendingNodeId);
116
118
  const iv = nonce;
117
119
  return {
118
120
  key,
119
121
  iv,
120
- ...decryptAES128CCM(key, iv, ciphertext, authData, authTag),
122
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
123
+ ...decryptAES128CCMSync(key, iv, ciphertext, authData, authTag),
121
124
  };
122
125
  };
123
126
  const getNonceAndDecrypt = () => {
127
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
124
128
  const iv = securityManager.nextNonce(sendingNodeId);
125
129
  return decryptWithNonce(iv);
126
130
  };
@@ -172,6 +176,7 @@ function decryptSinglecast(ctx, securityManager, sendingNodeId, curSequenceNumbe
172
176
  const isBootstrappingNode = securityManager.tempKeys.has(sendingNodeId);
173
177
  if (isBootstrappingNode) {
174
178
  // We're currently bootstrapping the node, it might be using a temporary key
179
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
175
180
  securityManager.initializeTempSPAN(sendingNodeId, senderEI, receiverEI);
176
181
  const ret = getNonceAndDecrypt();
177
182
  // Decryption with the temporary key worked
@@ -203,6 +208,7 @@ function decryptSinglecast(ctx, securityManager, sendingNodeId, curSequenceNumbe
203
208
  continue;
204
209
  }
205
210
  // Initialize an SPAN with that security class
211
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
206
212
  securityManager.initializeSPAN(sendingNodeId, secClass, senderEI, receiverEI);
207
213
  const ret = getNonceAndDecrypt();
208
214
  // It worked, return the result
@@ -230,17 +236,212 @@ function decryptSinglecast(ctx, securityManager, sendingNodeId, curSequenceNumbe
230
236
  securityClass: undefined,
231
237
  };
232
238
  }
233
- function decryptMulticast(sendingNodeId, securityManager, groupId, ciphertext, authData, authTag) {
239
+ async function decryptSinglecastAsync(ctx, securityManager, sendingNodeId, curSequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions) {
240
+ const decryptWithNonce = async (nonce) => {
241
+ const { keyCCM: key } = securityManager.getKeysForNode(sendingNodeId);
242
+ const iv = nonce;
243
+ return {
244
+ key,
245
+ iv,
246
+ ...(await decryptAES128CCMAsync(ciphertext, key, iv, authData, authTag)),
247
+ };
248
+ };
249
+ const getNonceAndDecrypt = async () => {
250
+ const iv = await securityManager.nextNonceAsync(sendingNodeId);
251
+ return decryptWithNonce(iv);
252
+ };
253
+ if (spanState.type === SPANState.SPAN) {
254
+ // There SHOULD be a shared SPAN between both parties. But experience has shown that both could have
255
+ // sent a command at roughly the same time, using the same SPAN for encryption.
256
+ // To avoid a nasty desync and both nodes trying to resync at the same time, causing message loss,
257
+ // we accept commands encrypted with the previous SPAN under very specific circumstances:
258
+ if (
259
+ // The previous SPAN is still known, i.e. the node didn't send another command that was successfully decrypted
260
+ !!spanState.currentSPAN
261
+ // it is still valid
262
+ && spanState.currentSPAN.expires > highResTimestamp()
263
+ // The received command is exactly the next, expected one
264
+ && prevSequenceNumber != undefined
265
+ && curSequenceNumber === ((prevSequenceNumber + 1) & 0xff)
266
+ // And in case of a mock-based test, do this only on the controller
267
+ && !ctx.__internalIsMockNode) {
268
+ const nonce = spanState.currentSPAN.nonce;
269
+ spanState.currentSPAN = undefined;
270
+ // If we could decrypt this way, we're done...
271
+ const result = await decryptWithNonce(nonce);
272
+ if (result.authOK) {
273
+ return {
274
+ ...result,
275
+ securityClass: spanState.securityClass,
276
+ };
277
+ }
278
+ // ...otherwise, we need to try the normal way
279
+ }
280
+ else {
281
+ // forgetting the current SPAN shouldn't be necessary but better be safe than sorry
282
+ spanState.currentSPAN = undefined;
283
+ }
284
+ // This can only happen if the security class is known
285
+ return {
286
+ ...(await getNonceAndDecrypt()),
287
+ securityClass: spanState.securityClass,
288
+ };
289
+ }
290
+ else if (spanState.type === SPANState.LocalEI) {
291
+ // We've sent the other our receiver's EI and received its sender's EI,
292
+ // meaning we can now establish an SPAN
293
+ const senderEI = getSenderEI(extensions);
294
+ if (!senderEI)
295
+ failNoSPAN();
296
+ const receiverEI = spanState.receiverEI;
297
+ // How we do this depends on whether we know the security class of the other node
298
+ const isBootstrappingNode = securityManager.tempKeys.has(sendingNodeId);
299
+ if (isBootstrappingNode) {
300
+ // We're currently bootstrapping the node, it might be using a temporary key
301
+ await securityManager.initializeTempSPANAsync(sendingNodeId, senderEI, receiverEI);
302
+ const ret = await getNonceAndDecrypt();
303
+ // Decryption with the temporary key worked
304
+ if (ret.authOK) {
305
+ return {
306
+ ...ret,
307
+ securityClass: SecurityClass.Temporary,
308
+ };
309
+ }
310
+ // Reset the SPAN state and try with the recently granted security class
311
+ securityManager.setSPANState(sendingNodeId, spanState);
312
+ }
313
+ // When ending up here, one of two situations has occured:
314
+ // a) We've taken over an existing network and do not know the node's security class
315
+ // b) We know the security class, but we're about to establish a new SPAN. This may happen at a lower
316
+ // security class than the one the node normally uses, e.g. when we're being queried for securely
317
+ // supported CCs.
318
+ // In both cases, we should simply try decoding with multiple security classes, starting from the highest one.
319
+ // If this fails, we restore the previous (partial) SPAN state.
320
+ // Try all security classes where we do not definitely know that it was not granted
321
+ // While bootstrapping a node, we consider the key that is being exchanged (including S0) to be the highest. No need to look at others
322
+ const possibleSecurityClasses = isBootstrappingNode
323
+ ? [ctx.getHighestSecurityClass(sendingNodeId)]
324
+ : securityClassOrder.filter((s) => ctx.hasSecurityClass(sendingNodeId, s)
325
+ !== false);
326
+ for (const secClass of possibleSecurityClasses) {
327
+ // Skip security classes we don't have keys for
328
+ if (!securityManager.hasKeysForSecurityClass(secClass)) {
329
+ continue;
330
+ }
331
+ // Initialize an SPAN with that security class
332
+ await securityManager.initializeSPANAsync(sendingNodeId, secClass, senderEI, receiverEI);
333
+ const ret = await getNonceAndDecrypt();
334
+ // It worked, return the result
335
+ if (ret.authOK) {
336
+ // Also if we weren't sure before, we now know that the security class is granted
337
+ if (ctx.hasSecurityClass(sendingNodeId, secClass)
338
+ === undefined) {
339
+ ctx.setSecurityClass(sendingNodeId, secClass, true);
340
+ }
341
+ return {
342
+ ...ret,
343
+ securityClass: secClass,
344
+ };
345
+ }
346
+ else {
347
+ // Reset the SPAN state and try with the next security class
348
+ securityManager.setSPANState(sendingNodeId, spanState);
349
+ }
350
+ }
351
+ }
352
+ // Nothing worked, fail the decryption
353
+ return {
354
+ plaintext: new Uint8Array(),
355
+ authOK: false,
356
+ securityClass: undefined,
357
+ };
358
+ }
359
+ /** @deprecated Use {@link decryptMulticastAsync} instead */
360
+ function decryptMulticastSync(sendingNodeId, securityManager, groupId, ciphertext, authData, authTag) {
361
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
234
362
  const iv = securityManager.nextPeerMPAN(sendingNodeId, groupId);
235
363
  const { keyCCM: key } = securityManager.getKeysForNode(sendingNodeId);
236
364
  return {
237
365
  key,
238
366
  iv,
239
- ...decryptAES128CCM(key, iv, ciphertext, authData, authTag),
367
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
368
+ ...decryptAES128CCMSync(key, iv, ciphertext, authData, authTag),
240
369
  // The security class is irrelevant when decrypting multicast commands
241
370
  securityClass: undefined,
242
371
  };
243
372
  }
373
+ async function decryptMulticastAsync(sendingNodeId, securityManager, groupId, ciphertext, authData, authTag) {
374
+ const iv = await securityManager.nextPeerMPANAsync(sendingNodeId, groupId);
375
+ const { keyCCM: key } = securityManager.getKeysForNode(sendingNodeId);
376
+ return {
377
+ key,
378
+ iv,
379
+ ...(await decryptAES128CCMAsync(ciphertext, key, iv, authData, authTag)),
380
+ // The security class is irrelevant when decrypting multicast commands
381
+ securityClass: undefined,
382
+ };
383
+ }
384
+ function parseExtensions(buffer, wasEncrypted) {
385
+ const extensions = [];
386
+ let mustDiscardCommand = false;
387
+ let offset = 0;
388
+ parsing: while (true) {
389
+ if (buffer.length < offset + 2) {
390
+ // An S2 extension was expected, but the buffer is too short
391
+ mustDiscardCommand = true;
392
+ break parsing;
393
+ }
394
+ // The length field could be too large, which would cause part of the actual ciphertext
395
+ // to be ignored. Try to avoid this for known extensions by checking the actual and expected length.
396
+ const { actual: actualLength, expected: expectedLength } = Security2Extension
397
+ .getExtensionLength(buffer.subarray(offset));
398
+ // Parse the extension using the expected length if possible
399
+ const extensionLength = expectedLength ?? actualLength;
400
+ if (extensionLength < 2) {
401
+ // An S2 extension was expected, but the length is too short
402
+ mustDiscardCommand = true;
403
+ break parsing;
404
+ }
405
+ else if (extensionLength
406
+ > buffer.length
407
+ - offset
408
+ - (wasEncrypted
409
+ ? 0
410
+ : SECURITY_S2_AUTH_TAG_LENGTH)) {
411
+ // The supposed length is longer than the space the extensions may occupy
412
+ mustDiscardCommand = true;
413
+ break parsing;
414
+ }
415
+ const extensionData = buffer.subarray(offset, offset + extensionLength);
416
+ offset += extensionLength;
417
+ const ext = Security2Extension.parse(extensionData);
418
+ switch (validateS2Extension(ext, wasEncrypted)) {
419
+ case ValidateS2ExtensionResult.OK:
420
+ if (expectedLength != undefined
421
+ && actualLength !== expectedLength) {
422
+ // The extension length field does not match, ignore the extension
423
+ }
424
+ else {
425
+ extensions.push(ext);
426
+ }
427
+ break;
428
+ case ValidateS2ExtensionResult.DiscardExtension:
429
+ // Do nothing
430
+ break;
431
+ case ValidateS2ExtensionResult.DiscardCommand:
432
+ mustDiscardCommand = true;
433
+ break;
434
+ }
435
+ // Check if that was the last extension
436
+ if (!ext.moreToFollow)
437
+ break parsing;
438
+ }
439
+ return {
440
+ extensions,
441
+ mustDiscardCommand,
442
+ bytesRead: offset,
443
+ };
444
+ }
244
445
  function getDestinationIDTX() {
245
446
  if (this.isSinglecast())
246
447
  return this.nodeId;
@@ -302,7 +503,7 @@ let Security2CCAPI = (() => {
302
503
  if (!securityManager) {
303
504
  throw new ZWaveError(`Nonces can only be sent if secure communication is set up!`, ZWaveErrorCodes.Driver_NoSecurity);
304
505
  }
305
- const receiverEI = securityManager.generateNonce(this.endpoint.nodeId);
506
+ const receiverEI = await securityManager.generateNonceAsync(this.endpoint.nodeId);
306
507
  const cc = new Security2CCNonceReport({
307
508
  nodeId: this.endpoint.nodeId,
308
509
  endpointIndex: this.endpoint.index,
@@ -922,6 +1123,7 @@ let Security2CCMessageEncapsulation = (() => {
922
1123
  throw new ZWaveError("Multicast Security S2 encapsulation requires the MGRP extension", ZWaveErrorCodes.Security2CC_MissingExtension);
923
1124
  }
924
1125
  }
1126
+ /** @deprecated Use {@link fromAsync} instead */
925
1127
  static from(raw, ctx) {
926
1128
  const securityManager = assertSecurityRX(ctx);
927
1129
  validatePayload(raw.payload.length >= 2);
@@ -934,61 +1136,12 @@ let Security2CCMessageEncapsulation = (() => {
934
1136
  let offset = 2;
935
1137
  const extensions = [];
936
1138
  let mustDiscardCommand = false;
937
- const parseExtensions = (buffer, wasEncrypted) => {
938
- while (true) {
939
- if (buffer.length < offset + 2) {
940
- // An S2 extension was expected, but the buffer is too short
941
- mustDiscardCommand = true;
942
- return;
943
- }
944
- // The length field could be too large, which would cause part of the actual ciphertext
945
- // to be ignored. Try to avoid this for known extensions by checking the actual and expected length.
946
- const { actual: actualLength, expected: expectedLength } = Security2Extension
947
- .getExtensionLength(buffer.subarray(offset));
948
- // Parse the extension using the expected length if possible
949
- const extensionLength = expectedLength ?? actualLength;
950
- if (extensionLength < 2) {
951
- // An S2 extension was expected, but the length is too short
952
- mustDiscardCommand = true;
953
- return;
954
- }
955
- else if (extensionLength
956
- > buffer.length
957
- - offset
958
- - (wasEncrypted
959
- ? 0
960
- : SECURITY_S2_AUTH_TAG_LENGTH)) {
961
- // The supposed length is longer than the space the extensions may occupy
962
- mustDiscardCommand = true;
963
- return;
964
- }
965
- const extensionData = buffer.subarray(offset, offset + extensionLength);
966
- offset += extensionLength;
967
- const ext = Security2Extension.parse(extensionData);
968
- switch (validateS2Extension(ext, wasEncrypted)) {
969
- case ValidateS2ExtensionResult.OK:
970
- if (expectedLength != undefined
971
- && actualLength !== expectedLength) {
972
- // The extension length field does not match, ignore the extension
973
- }
974
- else {
975
- extensions.push(ext);
976
- }
977
- break;
978
- case ValidateS2ExtensionResult.DiscardExtension:
979
- // Do nothing
980
- break;
981
- case ValidateS2ExtensionResult.DiscardCommand:
982
- mustDiscardCommand = true;
983
- break;
984
- }
985
- // Check if that was the last extension
986
- if (!ext.moreToFollow)
987
- break;
988
- }
989
- };
990
- if (hasExtensions)
991
- parseExtensions(raw.payload, false);
1139
+ if (hasExtensions) {
1140
+ const parseResult = parseExtensions(raw.payload.subarray(offset), false);
1141
+ extensions.push(...parseResult.extensions);
1142
+ offset += parseResult.bytesRead;
1143
+ mustDiscardCommand = parseResult.mustDiscardCommand;
1144
+ }
992
1145
  const mcctx = (() => {
993
1146
  const multicastGroupId = getMulticastGroupId(extensions);
994
1147
  if (ctx.frameType === "multicast" || ctx.frameType === "broadcast") {
@@ -1008,9 +1161,11 @@ let Security2CCMessageEncapsulation = (() => {
1008
1161
  // we still need to increment the SPAN or MPAN state
1009
1162
  if (mustDiscardCommand) {
1010
1163
  if (mcctx.isMulticast) {
1164
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1011
1165
  securityManager.nextPeerMPAN(ctx.sourceNodeId, mcctx.groupId);
1012
1166
  }
1013
1167
  else {
1168
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1014
1169
  securityManager.nextNonce(ctx.sourceNodeId);
1015
1170
  }
1016
1171
  validatePayload.fail("Invalid S2 extension");
@@ -1043,7 +1198,9 @@ let Security2CCMessageEncapsulation = (() => {
1043
1198
  securityManager.storePeerMPAN(ctx.sourceNodeId, mcctx.groupId, { type: MPANState.OutOfSync });
1044
1199
  failNoMPAN();
1045
1200
  }
1046
- decrypt = () => decryptMulticast(ctx.sourceNodeId, securityManager, mcctx.groupId, ciphertext, authData, authTag);
1201
+ decrypt = () =>
1202
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1203
+ decryptMulticastSync(ctx.sourceNodeId, securityManager, mcctx.groupId, ciphertext, authData, authTag);
1047
1204
  }
1048
1205
  else {
1049
1206
  // Decrypt payload and verify integrity
@@ -1057,7 +1214,9 @@ let Security2CCMessageEncapsulation = (() => {
1057
1214
  // For now, do the same as if we didn't have any EI
1058
1215
  failNoSPAN();
1059
1216
  }
1060
- decrypt = () => decryptSinglecast(ctx, securityManager, ctx.sourceNodeId, sequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions);
1217
+ decrypt = () =>
1218
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1219
+ decryptSinglecastSync(ctx, securityManager, ctx.sourceNodeId, sequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions);
1061
1220
  }
1062
1221
  let plaintext;
1063
1222
  let authOK = false;
@@ -1108,8 +1267,12 @@ let Security2CCMessageEncapsulation = (() => {
1108
1267
  // Remember which security class was used to decrypt this message, so we can discard it later
1109
1268
  const securityClass = decryptionSecurityClass;
1110
1269
  offset = 0;
1111
- if (hasEncryptedExtensions)
1112
- parseExtensions(plaintext, true);
1270
+ if (hasEncryptedExtensions) {
1271
+ const parseResult = parseExtensions(plaintext, true);
1272
+ extensions.push(...parseResult.extensions);
1273
+ offset += parseResult.bytesRead;
1274
+ mustDiscardCommand = parseResult.mustDiscardCommand;
1275
+ }
1113
1276
  // Before we can continue, check if the command must be discarded
1114
1277
  if (mustDiscardCommand) {
1115
1278
  validatePayload.fail("Invalid extension");
@@ -1131,6 +1294,7 @@ let Security2CCMessageEncapsulation = (() => {
1131
1294
  // make sure this contains a complete CC command that's worth splitting
1132
1295
  validatePayload(decryptedCCBytes.length >= 2);
1133
1296
  // and deserialize the CC
1297
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1134
1298
  encapsulated = CommandClass.parse(decryptedCCBytes, ctx);
1135
1299
  }
1136
1300
  const ret = new Security2CCMessageEncapsulation({
@@ -1148,6 +1312,187 @@ let Security2CCMessageEncapsulation = (() => {
1148
1312
  ret.plaintext = decryptedCCBytes;
1149
1313
  return ret;
1150
1314
  }
1315
+ static async fromAsync(raw, ctx) {
1316
+ const securityManager = assertSecurityRX(ctx);
1317
+ validatePayload(raw.payload.length >= 2);
1318
+ // Check the sequence number to avoid duplicates
1319
+ const sequenceNumber = raw.payload[0];
1320
+ // Ensure the node has a security class
1321
+ validatePayload.withReason("No security class granted")(ctx.getHighestSecurityClass(ctx.sourceNodeId) !== SecurityClass.None);
1322
+ const hasExtensions = !!(raw.payload[1] & 0b1);
1323
+ const hasEncryptedExtensions = !!(raw.payload[1] & 0b10);
1324
+ let offset = 2;
1325
+ const extensions = [];
1326
+ let mustDiscardCommand = false;
1327
+ if (hasExtensions) {
1328
+ const parseResult = parseExtensions(raw.payload.subarray(offset), false);
1329
+ extensions.push(...parseResult.extensions);
1330
+ offset += parseResult.bytesRead;
1331
+ mustDiscardCommand = parseResult.mustDiscardCommand;
1332
+ }
1333
+ const mcctx = (() => {
1334
+ const multicastGroupId = getMulticastGroupId(extensions);
1335
+ if (ctx.frameType === "multicast" || ctx.frameType === "broadcast") {
1336
+ if (multicastGroupId == undefined) {
1337
+ validatePayload.fail("Multicast frames without MGRP extension");
1338
+ }
1339
+ return {
1340
+ isMulticast: true,
1341
+ groupId: multicastGroupId,
1342
+ };
1343
+ }
1344
+ else {
1345
+ return { isMulticast: false, groupId: multicastGroupId };
1346
+ }
1347
+ })();
1348
+ // If a command is to be discarded before decryption,
1349
+ // we still need to increment the SPAN or MPAN state
1350
+ if (mustDiscardCommand) {
1351
+ if (mcctx.isMulticast) {
1352
+ await securityManager.nextPeerMPANAsync(ctx.sourceNodeId, mcctx.groupId);
1353
+ }
1354
+ else {
1355
+ await securityManager.nextNonceAsync(ctx.sourceNodeId);
1356
+ }
1357
+ validatePayload.fail("Invalid S2 extension");
1358
+ }
1359
+ let prevSequenceNumber;
1360
+ let mpanState;
1361
+ if (mcctx.isMulticast) {
1362
+ mpanState = securityManager.getPeerMPAN(ctx.sourceNodeId, mcctx.groupId);
1363
+ }
1364
+ else {
1365
+ // Don't accept duplicate Singlecast commands
1366
+ prevSequenceNumber = validateSequenceNumber(securityManager, ctx.sourceNodeId, sequenceNumber);
1367
+ // When a node receives a singlecast message after a multicast group was marked out of sync,
1368
+ // it must forget about the group.
1369
+ if (mcctx.groupId == undefined) {
1370
+ securityManager.resetOutOfSyncMPANs(ctx.sourceNodeId);
1371
+ }
1372
+ }
1373
+ const unencryptedPayload = raw.payload.subarray(0, offset);
1374
+ const ciphertext = raw.payload.subarray(offset, -SECURITY_S2_AUTH_TAG_LENGTH);
1375
+ const authTag = raw.payload.subarray(-SECURITY_S2_AUTH_TAG_LENGTH);
1376
+ const messageLength = 2 /* CommandClass.computeEncapsulationOverhead() */
1377
+ + raw.payload.length;
1378
+ const authData = getAuthenticationData(ctx.sourceNodeId, getDestinationIDRX(ctx, extensions), ctx.homeId, messageLength, unencryptedPayload);
1379
+ let decrypt;
1380
+ if (mcctx.isMulticast) {
1381
+ // For incoming multicast commands, make sure we have an MPAN
1382
+ if (mpanState?.type !== MPANState.MPAN) {
1383
+ // If we don't, mark the MPAN as out of sync, so we can respond accordingly on the singlecast followup
1384
+ securityManager.storePeerMPAN(ctx.sourceNodeId, mcctx.groupId, { type: MPANState.OutOfSync });
1385
+ failNoMPAN();
1386
+ }
1387
+ decrypt = () => decryptMulticastAsync(ctx.sourceNodeId, securityManager, mcctx.groupId, ciphertext, authData, authTag);
1388
+ }
1389
+ else {
1390
+ // Decrypt payload and verify integrity
1391
+ const spanState = securityManager.getSPANState(ctx.sourceNodeId);
1392
+ // If we are not able to establish an SPAN yet, fail the decryption
1393
+ if (spanState.type === SPANState.None) {
1394
+ failNoSPAN();
1395
+ }
1396
+ else if (spanState.type === SPANState.RemoteEI) {
1397
+ // TODO: The specs are not clear how to handle this case
1398
+ // For now, do the same as if we didn't have any EI
1399
+ failNoSPAN();
1400
+ }
1401
+ decrypt = () => decryptSinglecastAsync(ctx, securityManager, ctx.sourceNodeId, sequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions);
1402
+ }
1403
+ let plaintext;
1404
+ let authOK = false;
1405
+ let key;
1406
+ let iv;
1407
+ let decryptionSecurityClass;
1408
+ // If the Receiver is unable to authenticate the singlecast message with the current SPAN,
1409
+ // the Receiver SHOULD try decrypting the message with one or more of the following SPAN values,
1410
+ // stopping when decryption is successful or the maximum number of iterations is reached.
1411
+ // If the Receiver is unable to decrypt the S2 MC frame with the current MPAN, the Receiver MAY try
1412
+ // decrypting the frame with one or more of the subsequent MPAN values, stopping when decryption is
1413
+ // successful or the maximum number of iterations is reached.
1414
+ const decryptAttempts = mcctx.isMulticast
1415
+ ? MAX_DECRYPT_ATTEMPTS_MULTICAST
1416
+ : mcctx.groupId != undefined
1417
+ ? MAX_DECRYPT_ATTEMPTS_SC_FOLLOWUP
1418
+ : MAX_DECRYPT_ATTEMPTS_SINGLECAST;
1419
+ for (let i = 0; i < decryptAttempts; i++) {
1420
+ ({
1421
+ plaintext,
1422
+ authOK,
1423
+ key,
1424
+ iv,
1425
+ securityClass: decryptionSecurityClass,
1426
+ } = await decrypt());
1427
+ if (!!authOK && !!plaintext)
1428
+ break;
1429
+ // No need to try further SPANs if we just got the sender's EI
1430
+ if (!!getSenderEI(extensions))
1431
+ break;
1432
+ }
1433
+ // If authentication fails, do so with an error code that instructs the
1434
+ // applHost to tell the node we have no nonce
1435
+ if (!authOK || !plaintext) {
1436
+ if (mcctx.isMulticast) {
1437
+ // Mark the MPAN as out of sync
1438
+ securityManager.storePeerMPAN(ctx.sourceNodeId, mcctx.groupId, { type: MPANState.OutOfSync });
1439
+ validatePayload.fail(ZWaveErrorCodes.Security2CC_CannotDecodeMulticast);
1440
+ }
1441
+ else {
1442
+ validatePayload.fail(ZWaveErrorCodes.Security2CC_CannotDecode);
1443
+ }
1444
+ }
1445
+ else if (!mcctx.isMulticast && mcctx.groupId != undefined) {
1446
+ // After reception of a singlecast followup, the MPAN state must be increased
1447
+ securityManager.tryIncrementPeerMPAN(ctx.sourceNodeId, mcctx.groupId);
1448
+ }
1449
+ // Remember which security class was used to decrypt this message, so we can discard it later
1450
+ const securityClass = decryptionSecurityClass;
1451
+ offset = 0;
1452
+ if (hasEncryptedExtensions) {
1453
+ const parseResult = parseExtensions(plaintext, true);
1454
+ extensions.push(...parseResult.extensions);
1455
+ offset += parseResult.bytesRead;
1456
+ mustDiscardCommand = parseResult.mustDiscardCommand;
1457
+ }
1458
+ // Before we can continue, check if the command must be discarded
1459
+ if (mustDiscardCommand) {
1460
+ validatePayload.fail("Invalid extension");
1461
+ }
1462
+ // If the MPAN extension was received, store the MPAN
1463
+ if (!mcctx.isMulticast) {
1464
+ const mpanExtension = extensions.find((e) => e instanceof MPANExtension);
1465
+ if (mpanExtension) {
1466
+ securityManager.storePeerMPAN(ctx.sourceNodeId, mpanExtension.groupId, {
1467
+ type: MPANState.MPAN,
1468
+ currentMPAN: mpanExtension.innerMPANState,
1469
+ });
1470
+ }
1471
+ }
1472
+ // Not every S2 message includes an encapsulated CC
1473
+ const decryptedCCBytes = plaintext.subarray(offset);
1474
+ let encapsulated;
1475
+ if (decryptedCCBytes.length > 0) {
1476
+ // make sure this contains a complete CC command that's worth splitting
1477
+ validatePayload(decryptedCCBytes.length >= 2);
1478
+ // and deserialize the CC
1479
+ encapsulated = await CommandClass.parseAsync(decryptedCCBytes, ctx);
1480
+ }
1481
+ const ret = new Security2CCMessageEncapsulation({
1482
+ nodeId: ctx.sourceNodeId,
1483
+ sequenceNumber,
1484
+ securityClass,
1485
+ extensions,
1486
+ encapsulated,
1487
+ });
1488
+ // Remember for debugging purposes
1489
+ ret.key = key;
1490
+ ret.iv = iv;
1491
+ ret.authData = authData;
1492
+ ret.authTag = authTag;
1493
+ ret.plaintext = decryptedCCBytes;
1494
+ return ret;
1495
+ }
1151
1496
  securityClass;
1152
1497
  // Only used for testing/debugging purposes
1153
1498
  key;
@@ -1187,7 +1532,8 @@ let Security2CCMessageEncapsulation = (() => {
1187
1532
  getMulticastGroupId() {
1188
1533
  return getMulticastGroupId(this.extensions);
1189
1534
  }
1190
- maybeAddSPANExtension(ctx, securityManager) {
1535
+ /** @deprecated Use {@link maybeAddSPANExtensionAsync} instead */
1536
+ maybeAddSPANExtensionSync(ctx, securityManager) {
1191
1537
  if (!this.isSinglecast())
1192
1538
  return;
1193
1539
  const receiverNodeId = this.nodeId;
@@ -1200,12 +1546,14 @@ let Security2CCMessageEncapsulation = (() => {
1200
1546
  else if (spanState.type === SPANState.RemoteEI) {
1201
1547
  // We have the receiver's EI, generate our input and send it over
1202
1548
  // With both, we can create an SPAN
1549
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1203
1550
  const senderEI = securityManager.generateNonce(undefined);
1204
1551
  const receiverEI = spanState.receiverEI;
1205
1552
  // While bootstrapping a node, prefer the temporary key, unless the
1206
1553
  // specific command specifies a security class
1207
1554
  if (this.securityClass == undefined
1208
1555
  && securityManager.tempKeys.has(receiverNodeId)) {
1556
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1209
1557
  securityManager.initializeTempSPAN(receiverNodeId, senderEI, receiverEI);
1210
1558
  }
1211
1559
  else {
@@ -1214,6 +1562,7 @@ let Security2CCMessageEncapsulation = (() => {
1214
1562
  if (securityClass == undefined) {
1215
1563
  throw new ZWaveError("No security class defined for this command!", ZWaveErrorCodes.Security2CC_NoSPAN);
1216
1564
  }
1565
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1217
1566
  securityManager.initializeSPAN(receiverNodeId, securityClass, senderEI, receiverEI);
1218
1567
  }
1219
1568
  // Add or update the SPAN extension
@@ -1227,11 +1576,53 @@ let Security2CCMessageEncapsulation = (() => {
1227
1576
  }
1228
1577
  }
1229
1578
  }
1579
+ async maybeAddSPANExtensionAsync(ctx, securityManager) {
1580
+ if (!this.isSinglecast())
1581
+ return;
1582
+ const receiverNodeId = this.nodeId;
1583
+ const spanState = securityManager.getSPANState(receiverNodeId);
1584
+ if (spanState.type === SPANState.None
1585
+ || spanState.type === SPANState.LocalEI) {
1586
+ // Can't do anything here if we don't have the receiver's EI
1587
+ throw new ZWaveError(`Security S2 CC requires the receiver's nonce to be sent!`, ZWaveErrorCodes.Security2CC_NoSPAN);
1588
+ }
1589
+ else if (spanState.type === SPANState.RemoteEI) {
1590
+ // We have the receiver's EI, generate our input and send it over
1591
+ // With both, we can create an SPAN
1592
+ const senderEI = await securityManager.generateNonceAsync(undefined);
1593
+ const receiverEI = spanState.receiverEI;
1594
+ // While bootstrapping a node, prefer the temporary key, unless the
1595
+ // specific command specifies a security class
1596
+ if (this.securityClass == undefined
1597
+ && securityManager.tempKeys.has(receiverNodeId)) {
1598
+ await securityManager.initializeTempSPANAsync(receiverNodeId, senderEI, receiverEI);
1599
+ }
1600
+ else {
1601
+ const securityClass = this.securityClass
1602
+ ?? ctx.getHighestSecurityClass(receiverNodeId);
1603
+ if (securityClass == undefined) {
1604
+ throw new ZWaveError("No security class defined for this command!", ZWaveErrorCodes.Security2CC_NoSPAN);
1605
+ }
1606
+ await securityManager.initializeSPANAsync(receiverNodeId, securityClass, senderEI, receiverEI);
1607
+ }
1608
+ // Add or update the SPAN extension
1609
+ let spanExtension = this.extensions.find((e) => e instanceof SPANExtension);
1610
+ if (spanExtension) {
1611
+ spanExtension.senderEI = senderEI;
1612
+ }
1613
+ else {
1614
+ spanExtension = new SPANExtension({ senderEI });
1615
+ this.extensions.push(spanExtension);
1616
+ }
1617
+ }
1618
+ }
1619
+ /** @deprecated Use {@link serializeAsync} instead */
1230
1620
  serialize(ctx) {
1231
1621
  const securityManager = assertSecurityTX(ctx, this.nodeId);
1232
1622
  this.ensureSequenceNumber(securityManager);
1233
1623
  // Include Sender EI in the command if we only have the receiver's EI
1234
- this.maybeAddSPANExtension(ctx, securityManager);
1624
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1625
+ this.maybeAddSPANExtensionSync(ctx, securityManager);
1235
1626
  const unencryptedExtensions = this.extensions.filter((e) => !e.isEncrypted());
1236
1627
  const encryptedExtensions = this.extensions.filter((e) => e.isEncrypted());
1237
1628
  const unencryptedPayload = Bytes.concat([
@@ -1242,6 +1633,7 @@ let Security2CCMessageEncapsulation = (() => {
1242
1633
  ]),
1243
1634
  ...unencryptedExtensions.map((e, index) => e.serialize(index < unencryptedExtensions.length - 1)),
1244
1635
  ]);
1636
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1245
1637
  const serializedCC = this.encapsulated?.serialize(ctx)
1246
1638
  ?? new Bytes();
1247
1639
  const plaintextPayload = Bytes.concat([
@@ -1259,6 +1651,7 @@ let Security2CCMessageEncapsulation = (() => {
1259
1651
  // Singlecast:
1260
1652
  // Generate a nonce for encryption, and remember it to attempt decryption
1261
1653
  // of potential in-flight messages from the target node.
1654
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1262
1655
  iv = securityManager.nextNonce(this.nodeId, true);
1263
1656
  const { keyCCM } =
1264
1657
  // Prefer the overridden security class if it was given
@@ -1269,11 +1662,13 @@ let Security2CCMessageEncapsulation = (() => {
1269
1662
  }
1270
1663
  else {
1271
1664
  // Multicast:
1665
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1272
1666
  const keyAndIV = securityManager.getMulticastKeyAndIV(destinationTag);
1273
1667
  key = keyAndIV.key;
1274
1668
  iv = keyAndIV.iv;
1275
1669
  }
1276
- const { ciphertext: ciphertextPayload, authTag } = encryptAES128CCM(key, iv, plaintextPayload, authData, SECURITY_S2_AUTH_TAG_LENGTH);
1670
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1671
+ const { ciphertext: ciphertextPayload, authTag } = encryptAES128CCMSync(key, iv, plaintextPayload, authData, SECURITY_S2_AUTH_TAG_LENGTH);
1277
1672
  // Remember key and IV for debugging purposes
1278
1673
  this.key = key;
1279
1674
  this.iv = iv;
@@ -1285,8 +1680,69 @@ let Security2CCMessageEncapsulation = (() => {
1285
1680
  ciphertextPayload,
1286
1681
  authTag,
1287
1682
  ]);
1683
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1288
1684
  return super.serialize(ctx);
1289
1685
  }
1686
+ async serializeAsync(ctx) {
1687
+ const securityManager = assertSecurityTX(ctx, this.nodeId);
1688
+ this.ensureSequenceNumber(securityManager);
1689
+ // Include Sender EI in the command if we only have the receiver's EI
1690
+ await this.maybeAddSPANExtensionAsync(ctx, securityManager);
1691
+ const unencryptedExtensions = this.extensions.filter((e) => !e.isEncrypted());
1692
+ const encryptedExtensions = this.extensions.filter((e) => e.isEncrypted());
1693
+ const unencryptedPayload = Bytes.concat([
1694
+ Bytes.from([
1695
+ this.sequenceNumber,
1696
+ (encryptedExtensions.length > 0 ? 0b10 : 0)
1697
+ | (unencryptedExtensions.length > 0 ? 1 : 0),
1698
+ ]),
1699
+ ...unencryptedExtensions.map((e, index) => e.serialize(index < unencryptedExtensions.length - 1)),
1700
+ ]);
1701
+ const serializedCC = (await this.encapsulated?.serializeAsync(ctx))
1702
+ ?? new Bytes();
1703
+ const plaintextPayload = Bytes.concat([
1704
+ ...encryptedExtensions.map((e, index) => e.serialize(index < encryptedExtensions.length - 1)),
1705
+ serializedCC,
1706
+ ]);
1707
+ // Generate the authentication data for CCM encryption
1708
+ const destinationTag = getDestinationIDTX.call(this);
1709
+ const messageLength = this.computeEncapsulationOverhead()
1710
+ + serializedCC.length;
1711
+ const authData = getAuthenticationData(ctx.ownNodeId, destinationTag, ctx.homeId, messageLength, unencryptedPayload);
1712
+ let key;
1713
+ let iv;
1714
+ if (this.isSinglecast()) {
1715
+ // Singlecast:
1716
+ // Generate a nonce for encryption, and remember it to attempt decryption
1717
+ // of potential in-flight messages from the target node.
1718
+ iv = await securityManager.nextNonceAsync(this.nodeId, true);
1719
+ const { keyCCM } =
1720
+ // Prefer the overridden security class if it was given
1721
+ this.securityClass != undefined
1722
+ ? securityManager.getKeysForSecurityClass(this.securityClass)
1723
+ : securityManager.getKeysForNode(this.nodeId);
1724
+ key = keyCCM;
1725
+ }
1726
+ else {
1727
+ // Multicast:
1728
+ const keyAndIV = await securityManager.getMulticastKeyAndIVAsync(destinationTag);
1729
+ key = keyAndIV.key;
1730
+ iv = keyAndIV.iv;
1731
+ }
1732
+ const { ciphertext: ciphertextPayload, authTag } = await encryptAES128CCMAsync(plaintextPayload, key, iv, authData, SECURITY_S2_AUTH_TAG_LENGTH);
1733
+ // Remember key and IV for debugging purposes
1734
+ this.key = key;
1735
+ this.iv = iv;
1736
+ this.authData = authData;
1737
+ this.authTag = authTag;
1738
+ this.ciphertext = ciphertextPayload;
1739
+ this.payload = Bytes.concat([
1740
+ unencryptedPayload,
1741
+ ciphertextPayload,
1742
+ authTag,
1743
+ ]);
1744
+ return super.serializeAsync(ctx);
1745
+ }
1290
1746
  computeEncapsulationOverhead() {
1291
1747
  // Security S2 adds:
1292
1748
  // * 1 byte sequence number
@@ -1422,6 +1878,7 @@ let Security2CCNonceReport = (() => {
1422
1878
  if (this.SOS) {
1423
1879
  this.payload = Bytes.concat([this.payload, this.receiverEI]);
1424
1880
  }
1881
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1425
1882
  return super.serialize(ctx);
1426
1883
  }
1427
1884
  toLogEntry(ctx) {
@@ -1484,6 +1941,7 @@ let Security2CCNonceGet = (() => {
1484
1941
  const securityManager = assertSecurityTX(ctx, this.nodeId);
1485
1942
  this.ensureSequenceNumber(securityManager);
1486
1943
  this.payload = Bytes.from([this.sequenceNumber]);
1944
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1487
1945
  return super.serialize(ctx);
1488
1946
  }
1489
1947
  toLogEntry(ctx) {
@@ -1560,6 +2018,7 @@ let Security2CCKEXReport = (() => {
1560
2018
  encodeBitMask(this.supportedECDHProfiles, 7, ECDHProfiles.Curve25519),
1561
2019
  encodeBitMask(this.requestedKeys, SecurityClass.S0_Legacy, SecurityClass.S2_Unauthenticated),
1562
2020
  ]);
2021
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1563
2022
  return super.serialize(ctx);
1564
2023
  }
1565
2024
  toLogEntry(ctx) {
@@ -1687,6 +2146,7 @@ let Security2CCKEXSet = (() => {
1687
2146
  encodeBitMask([this.selectedECDHProfile], 7, ECDHProfiles.Curve25519),
1688
2147
  encodeBitMask(this.grantedKeys, SecurityClass.S0_Legacy, SecurityClass.S2_Unauthenticated),
1689
2148
  ]);
2149
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1690
2150
  return super.serialize(ctx);
1691
2151
  }
1692
2152
  toLogEntry(ctx) {
@@ -1737,6 +2197,7 @@ let Security2CCKEXFail = (() => {
1737
2197
  failType;
1738
2198
  serialize(ctx) {
1739
2199
  this.payload = Bytes.from([this.failType]);
2200
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1740
2201
  return super.serialize(ctx);
1741
2202
  }
1742
2203
  toLogEntry(ctx) {
@@ -1786,6 +2247,7 @@ let Security2CCPublicKeyReport = (() => {
1786
2247
  Bytes.from([this.includingNode ? 1 : 0]),
1787
2248
  this.publicKey,
1788
2249
  ]);
2250
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1789
2251
  return super.serialize(ctx);
1790
2252
  }
1791
2253
  toLogEntry(ctx) {
@@ -1838,6 +2300,7 @@ let Security2CCNetworkKeyReport = (() => {
1838
2300
  securityClassToBitMask(this.grantedKey),
1839
2301
  this.networkKey,
1840
2302
  ]);
2303
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1841
2304
  return super.serialize(ctx);
1842
2305
  }
1843
2306
  toLogEntry(ctx) {
@@ -1884,6 +2347,7 @@ let Security2CCNetworkKeyGet = (() => {
1884
2347
  requestedKey;
1885
2348
  serialize(ctx) {
1886
2349
  this.payload = securityClassToBitMask(this.requestedKey);
2350
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1887
2351
  return super.serialize(ctx);
1888
2352
  }
1889
2353
  toLogEntry(ctx) {
@@ -1953,6 +2417,7 @@ let Security2CCTransferEnd = (() => {
1953
2417
  this.payload = Bytes.from([
1954
2418
  (this.keyVerified ? 0b10 : 0) + (this.keyRequestComplete ? 0b1 : 0),
1955
2419
  ]);
2420
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1956
2421
  return super.serialize(ctx);
1957
2422
  }
1958
2423
  toLogEntry(ctx) {
@@ -2002,6 +2467,7 @@ let Security2CCCommandsSupportedReport = (() => {
2002
2467
  supportedCCs;
2003
2468
  serialize(ctx) {
2004
2469
  this.payload = encodeCCList(this.supportedCCs, []);
2470
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
2005
2471
  return super.serialize(ctx);
2006
2472
  }
2007
2473
  toLogEntry(ctx) {