@zwave-js/cc 14.2.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.
@@ -100,7 +100,9 @@ export declare class Security2CCMessageEncapsulation extends Security2CC {
100
100
  getSenderEI(): Uint8Array | undefined;
101
101
  /** Returns the multicast group ID if this command contains an MGRP extension */
102
102
  getMulticastGroupId(): number | undefined;
103
- private maybeAddSPANExtension;
103
+ /** @deprecated Use {@link maybeAddSPANExtensionAsync} instead */
104
+ private maybeAddSPANExtensionSync;
105
+ private maybeAddSPANExtensionAsync;
104
106
  /** @deprecated Use {@link serializeAsync} instead */
105
107
  serialize(ctx: CCEncodingContext): Bytes;
106
108
  serializeAsync(ctx: CCEncodingContext): Promise<Bytes>;
@@ -99,6 +99,7 @@ function bitMaskToSecurityClass(buffer, offset) {
99
99
  (0, import_core.validatePayload)(keys.length === 1);
100
100
  return keys[0];
101
101
  }
102
+ const SECURITY_S2_AUTH_TAG_LENGTH = 8;
102
103
  function getAuthenticationData(sendingNodeId, destination, homeId, commandLength, unencryptedPayload) {
103
104
  const nodeIdSize = (0, import_core.isLongRangeNodeId)(sendingNodeId) || (0, import_core.isLongRangeNodeId)(destination) ? 2 : 1;
104
105
  const ret = new import_safe2.Bytes(2 * nodeIdSize + 6 + unencryptedPayload.length);
@@ -145,14 +146,15 @@ function assertSecurityTX(ctx, destination) {
145
146
  }
146
147
  return ret;
147
148
  }
148
- function decryptSinglecast(ctx, securityManager, sendingNodeId, curSequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions) {
149
+ function decryptSinglecastSync(ctx, securityManager, sendingNodeId, curSequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions) {
149
150
  const decryptWithNonce = (nonce) => {
150
151
  const { keyCCM: key } = securityManager.getKeysForNode(sendingNodeId);
151
152
  const iv = nonce;
152
153
  return {
153
154
  key,
154
155
  iv,
155
- ...(0, import_core.decryptAES128CCM)(key, iv, ciphertext, authData, authTag)
156
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
157
+ ...(0, import_core.decryptAES128CCMSync)(key, iv, ciphertext, authData, authTag)
156
158
  };
157
159
  };
158
160
  const getNonceAndDecrypt = () => {
@@ -223,17 +225,150 @@ function decryptSinglecast(ctx, securityManager, sendingNodeId, curSequenceNumbe
223
225
  securityClass: void 0
224
226
  };
225
227
  }
226
- function decryptMulticast(sendingNodeId, securityManager, groupId, ciphertext, authData, authTag) {
228
+ async function decryptSinglecastAsync(ctx, securityManager, sendingNodeId, curSequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions) {
229
+ const decryptWithNonce = async (nonce) => {
230
+ const { keyCCM: key } = securityManager.getKeysForNode(sendingNodeId);
231
+ const iv = nonce;
232
+ return {
233
+ key,
234
+ iv,
235
+ ...await (0, import_core.decryptAES128CCMAsync)(ciphertext, key, iv, authData, authTag)
236
+ };
237
+ };
238
+ const getNonceAndDecrypt = async () => {
239
+ const iv = await securityManager.nextNonceAsync(sendingNodeId);
240
+ return decryptWithNonce(iv);
241
+ };
242
+ if (spanState.type === import_core.SPANState.SPAN) {
243
+ if (
244
+ // The previous SPAN is still known, i.e. the node didn't send another command that was successfully decrypted
245
+ !!spanState.currentSPAN && spanState.currentSPAN.expires > (0, import_core.highResTimestamp)() && prevSequenceNumber != void 0 && curSequenceNumber === (prevSequenceNumber + 1 & 255) && !ctx.__internalIsMockNode
246
+ ) {
247
+ const nonce = spanState.currentSPAN.nonce;
248
+ spanState.currentSPAN = void 0;
249
+ const result = await decryptWithNonce(nonce);
250
+ if (result.authOK) {
251
+ return {
252
+ ...result,
253
+ securityClass: spanState.securityClass
254
+ };
255
+ }
256
+ } else {
257
+ spanState.currentSPAN = void 0;
258
+ }
259
+ return {
260
+ ...await getNonceAndDecrypt(),
261
+ securityClass: spanState.securityClass
262
+ };
263
+ } else if (spanState.type === import_core.SPANState.LocalEI) {
264
+ const senderEI = getSenderEI(extensions);
265
+ if (!senderEI)
266
+ failNoSPAN();
267
+ const receiverEI = spanState.receiverEI;
268
+ const isBootstrappingNode = securityManager.tempKeys.has(sendingNodeId);
269
+ if (isBootstrappingNode) {
270
+ await securityManager.initializeTempSPANAsync(sendingNodeId, senderEI, receiverEI);
271
+ const ret = await getNonceAndDecrypt();
272
+ if (ret.authOK) {
273
+ return {
274
+ ...ret,
275
+ securityClass: import_core.SecurityClass.Temporary
276
+ };
277
+ }
278
+ securityManager.setSPANState(sendingNodeId, spanState);
279
+ }
280
+ const possibleSecurityClasses = isBootstrappingNode ? [ctx.getHighestSecurityClass(sendingNodeId)] : import_core.securityClassOrder.filter((s) => ctx.hasSecurityClass(sendingNodeId, s) !== false);
281
+ for (const secClass of possibleSecurityClasses) {
282
+ if (!securityManager.hasKeysForSecurityClass(secClass)) {
283
+ continue;
284
+ }
285
+ await securityManager.initializeSPANAsync(sendingNodeId, secClass, senderEI, receiverEI);
286
+ const ret = await getNonceAndDecrypt();
287
+ if (ret.authOK) {
288
+ if (ctx.hasSecurityClass(sendingNodeId, secClass) === void 0) {
289
+ ctx.setSecurityClass(sendingNodeId, secClass, true);
290
+ }
291
+ return {
292
+ ...ret,
293
+ securityClass: secClass
294
+ };
295
+ } else {
296
+ securityManager.setSPANState(sendingNodeId, spanState);
297
+ }
298
+ }
299
+ }
300
+ return {
301
+ plaintext: new Uint8Array(),
302
+ authOK: false,
303
+ securityClass: void 0
304
+ };
305
+ }
306
+ function decryptMulticastSync(sendingNodeId, securityManager, groupId, ciphertext, authData, authTag) {
227
307
  const iv = securityManager.nextPeerMPAN(sendingNodeId, groupId);
228
308
  const { keyCCM: key } = securityManager.getKeysForNode(sendingNodeId);
229
309
  return {
230
310
  key,
231
311
  iv,
232
- ...(0, import_core.decryptAES128CCM)(key, iv, ciphertext, authData, authTag),
312
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
313
+ ...(0, import_core.decryptAES128CCMSync)(key, iv, ciphertext, authData, authTag),
233
314
  // The security class is irrelevant when decrypting multicast commands
234
315
  securityClass: void 0
235
316
  };
236
317
  }
318
+ async function decryptMulticastAsync(sendingNodeId, securityManager, groupId, ciphertext, authData, authTag) {
319
+ const iv = await securityManager.nextPeerMPANAsync(sendingNodeId, groupId);
320
+ const { keyCCM: key } = securityManager.getKeysForNode(sendingNodeId);
321
+ return {
322
+ key,
323
+ iv,
324
+ ...await (0, import_core.decryptAES128CCMAsync)(ciphertext, key, iv, authData, authTag),
325
+ // The security class is irrelevant when decrypting multicast commands
326
+ securityClass: void 0
327
+ };
328
+ }
329
+ function parseExtensions(buffer, wasEncrypted) {
330
+ const extensions = [];
331
+ let mustDiscardCommand = false;
332
+ let offset = 0;
333
+ parsing: while (true) {
334
+ if (buffer.length < offset + 2) {
335
+ mustDiscardCommand = true;
336
+ break parsing;
337
+ }
338
+ const { actual: actualLength, expected: expectedLength } = import_Extension.Security2Extension.getExtensionLength(buffer.subarray(offset));
339
+ const extensionLength = expectedLength ?? actualLength;
340
+ if (extensionLength < 2) {
341
+ mustDiscardCommand = true;
342
+ break parsing;
343
+ } else if (extensionLength > buffer.length - offset - (wasEncrypted ? 0 : SECURITY_S2_AUTH_TAG_LENGTH)) {
344
+ mustDiscardCommand = true;
345
+ break parsing;
346
+ }
347
+ const extensionData = buffer.subarray(offset, offset + extensionLength);
348
+ offset += extensionLength;
349
+ const ext = import_Extension.Security2Extension.parse(extensionData);
350
+ switch ((0, import_Extension.validateS2Extension)(ext, wasEncrypted)) {
351
+ case import_Extension.ValidateS2ExtensionResult.OK:
352
+ if (expectedLength != void 0 && actualLength !== expectedLength) {
353
+ } else {
354
+ extensions.push(ext);
355
+ }
356
+ break;
357
+ case import_Extension.ValidateS2ExtensionResult.DiscardExtension:
358
+ break;
359
+ case import_Extension.ValidateS2ExtensionResult.DiscardCommand:
360
+ mustDiscardCommand = true;
361
+ break;
362
+ }
363
+ if (!ext.moreToFollow)
364
+ break parsing;
365
+ }
366
+ return {
367
+ extensions,
368
+ mustDiscardCommand,
369
+ bytesRead: offset
370
+ };
371
+ }
237
372
  function getDestinationIDTX() {
238
373
  if (this.isSinglecast())
239
374
  return this.nodeId;
@@ -292,7 +427,7 @@ let Security2CCAPI = (() => {
292
427
  if (!securityManager) {
293
428
  throw new import_core.ZWaveError(`Nonces can only be sent if secure communication is set up!`, import_core.ZWaveErrorCodes.Driver_NoSecurity);
294
429
  }
295
- const receiverEI = securityManager.generateNonce(this.endpoint.nodeId);
430
+ const receiverEI = await securityManager.generateNonceAsync(this.endpoint.nodeId);
296
431
  const cc = new Security2CCNonceReport({
297
432
  nodeId: this.endpoint.nodeId,
298
433
  endpointIndex: this.endpoint.index,
@@ -843,43 +978,12 @@ let Security2CCMessageEncapsulation = (() => {
843
978
  let offset = 2;
844
979
  const extensions = [];
845
980
  let mustDiscardCommand = false;
846
- const parseExtensions = (buffer, wasEncrypted) => {
847
- while (true) {
848
- if (buffer.length < offset + 2) {
849
- mustDiscardCommand = true;
850
- return;
851
- }
852
- const { actual: actualLength, expected: expectedLength } = import_Extension.Security2Extension.getExtensionLength(buffer.subarray(offset));
853
- const extensionLength = expectedLength ?? actualLength;
854
- if (extensionLength < 2) {
855
- mustDiscardCommand = true;
856
- return;
857
- } else if (extensionLength > buffer.length - offset - (wasEncrypted ? 0 : import_core.SECURITY_S2_AUTH_TAG_LENGTH)) {
858
- mustDiscardCommand = true;
859
- return;
860
- }
861
- const extensionData = buffer.subarray(offset, offset + extensionLength);
862
- offset += extensionLength;
863
- const ext = import_Extension.Security2Extension.parse(extensionData);
864
- switch ((0, import_Extension.validateS2Extension)(ext, wasEncrypted)) {
865
- case import_Extension.ValidateS2ExtensionResult.OK:
866
- if (expectedLength != void 0 && actualLength !== expectedLength) {
867
- } else {
868
- extensions.push(ext);
869
- }
870
- break;
871
- case import_Extension.ValidateS2ExtensionResult.DiscardExtension:
872
- break;
873
- case import_Extension.ValidateS2ExtensionResult.DiscardCommand:
874
- mustDiscardCommand = true;
875
- break;
876
- }
877
- if (!ext.moreToFollow)
878
- break;
879
- }
880
- };
881
- if (hasExtensions)
882
- parseExtensions(raw.payload, false);
981
+ if (hasExtensions) {
982
+ const parseResult = parseExtensions(raw.payload.subarray(offset), false);
983
+ extensions.push(...parseResult.extensions);
984
+ offset += parseResult.bytesRead;
985
+ mustDiscardCommand = parseResult.mustDiscardCommand;
986
+ }
883
987
  const mcctx = (() => {
884
988
  const multicastGroupId = getMulticastGroupId(extensions);
885
989
  if (ctx.frameType === "multicast" || ctx.frameType === "broadcast") {
@@ -913,8 +1017,8 @@ let Security2CCMessageEncapsulation = (() => {
913
1017
  }
914
1018
  }
915
1019
  const unencryptedPayload = raw.payload.subarray(0, offset);
916
- const ciphertext = raw.payload.subarray(offset, -import_core.SECURITY_S2_AUTH_TAG_LENGTH);
917
- const authTag = raw.payload.subarray(-import_core.SECURITY_S2_AUTH_TAG_LENGTH);
1020
+ const ciphertext = raw.payload.subarray(offset, -SECURITY_S2_AUTH_TAG_LENGTH);
1021
+ const authTag = raw.payload.subarray(-SECURITY_S2_AUTH_TAG_LENGTH);
918
1022
  const messageLength = 2 + raw.payload.length;
919
1023
  const authData = getAuthenticationData(ctx.sourceNodeId, getDestinationIDRX(ctx, extensions), ctx.homeId, messageLength, unencryptedPayload);
920
1024
  let decrypt;
@@ -923,7 +1027,10 @@ let Security2CCMessageEncapsulation = (() => {
923
1027
  securityManager.storePeerMPAN(ctx.sourceNodeId, mcctx.groupId, { type: import_core.MPANState.OutOfSync });
924
1028
  failNoMPAN();
925
1029
  }
926
- decrypt = () => decryptMulticast(ctx.sourceNodeId, securityManager, mcctx.groupId, ciphertext, authData, authTag);
1030
+ decrypt = () => (
1031
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1032
+ decryptMulticastSync(ctx.sourceNodeId, securityManager, mcctx.groupId, ciphertext, authData, authTag)
1033
+ );
927
1034
  } else {
928
1035
  const spanState = securityManager.getSPANState(ctx.sourceNodeId);
929
1036
  if (spanState.type === import_core.SPANState.None) {
@@ -931,7 +1038,10 @@ let Security2CCMessageEncapsulation = (() => {
931
1038
  } else if (spanState.type === import_core.SPANState.RemoteEI) {
932
1039
  failNoSPAN();
933
1040
  }
934
- decrypt = () => decryptSinglecast(ctx, securityManager, ctx.sourceNodeId, sequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions);
1041
+ decrypt = () => (
1042
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
1043
+ decryptSinglecastSync(ctx, securityManager, ctx.sourceNodeId, sequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions)
1044
+ );
935
1045
  }
936
1046
  let plaintext;
937
1047
  let authOK = false;
@@ -964,8 +1074,12 @@ let Security2CCMessageEncapsulation = (() => {
964
1074
  }
965
1075
  const securityClass = decryptionSecurityClass;
966
1076
  offset = 0;
967
- if (hasEncryptedExtensions)
968
- parseExtensions(plaintext, true);
1077
+ if (hasEncryptedExtensions) {
1078
+ const parseResult = parseExtensions(plaintext, true);
1079
+ extensions.push(...parseResult.extensions);
1080
+ offset += parseResult.bytesRead;
1081
+ mustDiscardCommand = parseResult.mustDiscardCommand;
1082
+ }
969
1083
  if (mustDiscardCommand) {
970
1084
  import_core.validatePayload.fail("Invalid extension");
971
1085
  }
@@ -1008,43 +1122,12 @@ let Security2CCMessageEncapsulation = (() => {
1008
1122
  let offset = 2;
1009
1123
  const extensions = [];
1010
1124
  let mustDiscardCommand = false;
1011
- const parseExtensions = (buffer, wasEncrypted) => {
1012
- while (true) {
1013
- if (buffer.length < offset + 2) {
1014
- mustDiscardCommand = true;
1015
- return;
1016
- }
1017
- const { actual: actualLength, expected: expectedLength } = import_Extension.Security2Extension.getExtensionLength(buffer.subarray(offset));
1018
- const extensionLength = expectedLength ?? actualLength;
1019
- if (extensionLength < 2) {
1020
- mustDiscardCommand = true;
1021
- return;
1022
- } else if (extensionLength > buffer.length - offset - (wasEncrypted ? 0 : import_core.SECURITY_S2_AUTH_TAG_LENGTH)) {
1023
- mustDiscardCommand = true;
1024
- return;
1025
- }
1026
- const extensionData = buffer.subarray(offset, offset + extensionLength);
1027
- offset += extensionLength;
1028
- const ext = import_Extension.Security2Extension.parse(extensionData);
1029
- switch ((0, import_Extension.validateS2Extension)(ext, wasEncrypted)) {
1030
- case import_Extension.ValidateS2ExtensionResult.OK:
1031
- if (expectedLength != void 0 && actualLength !== expectedLength) {
1032
- } else {
1033
- extensions.push(ext);
1034
- }
1035
- break;
1036
- case import_Extension.ValidateS2ExtensionResult.DiscardExtension:
1037
- break;
1038
- case import_Extension.ValidateS2ExtensionResult.DiscardCommand:
1039
- mustDiscardCommand = true;
1040
- break;
1041
- }
1042
- if (!ext.moreToFollow)
1043
- break;
1044
- }
1045
- };
1046
- if (hasExtensions)
1047
- parseExtensions(raw.payload, false);
1125
+ if (hasExtensions) {
1126
+ const parseResult = parseExtensions(raw.payload.subarray(offset), false);
1127
+ extensions.push(...parseResult.extensions);
1128
+ offset += parseResult.bytesRead;
1129
+ mustDiscardCommand = parseResult.mustDiscardCommand;
1130
+ }
1048
1131
  const mcctx = (() => {
1049
1132
  const multicastGroupId = getMulticastGroupId(extensions);
1050
1133
  if (ctx.frameType === "multicast" || ctx.frameType === "broadcast") {
@@ -1061,9 +1144,9 @@ let Security2CCMessageEncapsulation = (() => {
1061
1144
  })();
1062
1145
  if (mustDiscardCommand) {
1063
1146
  if (mcctx.isMulticast) {
1064
- securityManager.nextPeerMPAN(ctx.sourceNodeId, mcctx.groupId);
1147
+ await securityManager.nextPeerMPANAsync(ctx.sourceNodeId, mcctx.groupId);
1065
1148
  } else {
1066
- securityManager.nextNonce(ctx.sourceNodeId);
1149
+ await securityManager.nextNonceAsync(ctx.sourceNodeId);
1067
1150
  }
1068
1151
  import_core.validatePayload.fail("Invalid S2 extension");
1069
1152
  }
@@ -1078,8 +1161,8 @@ let Security2CCMessageEncapsulation = (() => {
1078
1161
  }
1079
1162
  }
1080
1163
  const unencryptedPayload = raw.payload.subarray(0, offset);
1081
- const ciphertext = raw.payload.subarray(offset, -import_core.SECURITY_S2_AUTH_TAG_LENGTH);
1082
- const authTag = raw.payload.subarray(-import_core.SECURITY_S2_AUTH_TAG_LENGTH);
1164
+ const ciphertext = raw.payload.subarray(offset, -SECURITY_S2_AUTH_TAG_LENGTH);
1165
+ const authTag = raw.payload.subarray(-SECURITY_S2_AUTH_TAG_LENGTH);
1083
1166
  const messageLength = 2 + raw.payload.length;
1084
1167
  const authData = getAuthenticationData(ctx.sourceNodeId, getDestinationIDRX(ctx, extensions), ctx.homeId, messageLength, unencryptedPayload);
1085
1168
  let decrypt;
@@ -1088,7 +1171,7 @@ let Security2CCMessageEncapsulation = (() => {
1088
1171
  securityManager.storePeerMPAN(ctx.sourceNodeId, mcctx.groupId, { type: import_core.MPANState.OutOfSync });
1089
1172
  failNoMPAN();
1090
1173
  }
1091
- decrypt = () => decryptMulticast(ctx.sourceNodeId, securityManager, mcctx.groupId, ciphertext, authData, authTag);
1174
+ decrypt = () => decryptMulticastAsync(ctx.sourceNodeId, securityManager, mcctx.groupId, ciphertext, authData, authTag);
1092
1175
  } else {
1093
1176
  const spanState = securityManager.getSPANState(ctx.sourceNodeId);
1094
1177
  if (spanState.type === import_core.SPANState.None) {
@@ -1096,7 +1179,7 @@ let Security2CCMessageEncapsulation = (() => {
1096
1179
  } else if (spanState.type === import_core.SPANState.RemoteEI) {
1097
1180
  failNoSPAN();
1098
1181
  }
1099
- decrypt = () => decryptSinglecast(ctx, securityManager, ctx.sourceNodeId, sequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions);
1182
+ decrypt = () => decryptSinglecastAsync(ctx, securityManager, ctx.sourceNodeId, sequenceNumber, prevSequenceNumber, ciphertext, authData, authTag, spanState, extensions);
1100
1183
  }
1101
1184
  let plaintext;
1102
1185
  let authOK = false;
@@ -1111,7 +1194,7 @@ let Security2CCMessageEncapsulation = (() => {
1111
1194
  key,
1112
1195
  iv,
1113
1196
  securityClass: decryptionSecurityClass
1114
- } = decrypt());
1197
+ } = await decrypt());
1115
1198
  if (!!authOK && !!plaintext)
1116
1199
  break;
1117
1200
  if (!!getSenderEI(extensions))
@@ -1129,8 +1212,12 @@ let Security2CCMessageEncapsulation = (() => {
1129
1212
  }
1130
1213
  const securityClass = decryptionSecurityClass;
1131
1214
  offset = 0;
1132
- if (hasEncryptedExtensions)
1133
- parseExtensions(plaintext, true);
1215
+ if (hasEncryptedExtensions) {
1216
+ const parseResult = parseExtensions(plaintext, true);
1217
+ extensions.push(...parseResult.extensions);
1218
+ offset += parseResult.bytesRead;
1219
+ mustDiscardCommand = parseResult.mustDiscardCommand;
1220
+ }
1134
1221
  if (mustDiscardCommand) {
1135
1222
  import_core.validatePayload.fail("Invalid extension");
1136
1223
  }
@@ -1200,7 +1287,8 @@ let Security2CCMessageEncapsulation = (() => {
1200
1287
  getMulticastGroupId() {
1201
1288
  return getMulticastGroupId(this.extensions);
1202
1289
  }
1203
- maybeAddSPANExtension(ctx, securityManager) {
1290
+ /** @deprecated Use {@link maybeAddSPANExtensionAsync} instead */
1291
+ maybeAddSPANExtensionSync(ctx, securityManager) {
1204
1292
  if (!this.isSinglecast())
1205
1293
  return;
1206
1294
  const receiverNodeId = this.nodeId;
@@ -1228,11 +1316,39 @@ let Security2CCMessageEncapsulation = (() => {
1228
1316
  }
1229
1317
  }
1230
1318
  }
1319
+ async maybeAddSPANExtensionAsync(ctx, securityManager) {
1320
+ if (!this.isSinglecast())
1321
+ return;
1322
+ const receiverNodeId = this.nodeId;
1323
+ const spanState = securityManager.getSPANState(receiverNodeId);
1324
+ if (spanState.type === import_core.SPANState.None || spanState.type === import_core.SPANState.LocalEI) {
1325
+ throw new import_core.ZWaveError(`Security S2 CC requires the receiver's nonce to be sent!`, import_core.ZWaveErrorCodes.Security2CC_NoSPAN);
1326
+ } else if (spanState.type === import_core.SPANState.RemoteEI) {
1327
+ const senderEI = await securityManager.generateNonceAsync(void 0);
1328
+ const receiverEI = spanState.receiverEI;
1329
+ if (this.securityClass == void 0 && securityManager.tempKeys.has(receiverNodeId)) {
1330
+ await securityManager.initializeTempSPANAsync(receiverNodeId, senderEI, receiverEI);
1331
+ } else {
1332
+ const securityClass = this.securityClass ?? ctx.getHighestSecurityClass(receiverNodeId);
1333
+ if (securityClass == void 0) {
1334
+ throw new import_core.ZWaveError("No security class defined for this command!", import_core.ZWaveErrorCodes.Security2CC_NoSPAN);
1335
+ }
1336
+ await securityManager.initializeSPANAsync(receiverNodeId, securityClass, senderEI, receiverEI);
1337
+ }
1338
+ let spanExtension = this.extensions.find((e) => e instanceof import_Extension.SPANExtension);
1339
+ if (spanExtension) {
1340
+ spanExtension.senderEI = senderEI;
1341
+ } else {
1342
+ spanExtension = new import_Extension.SPANExtension({ senderEI });
1343
+ this.extensions.push(spanExtension);
1344
+ }
1345
+ }
1346
+ }
1231
1347
  /** @deprecated Use {@link serializeAsync} instead */
1232
1348
  serialize(ctx) {
1233
1349
  const securityManager = assertSecurityTX(ctx, this.nodeId);
1234
1350
  this.ensureSequenceNumber(securityManager);
1235
- this.maybeAddSPANExtension(ctx, securityManager);
1351
+ this.maybeAddSPANExtensionSync(ctx, securityManager);
1236
1352
  const unencryptedExtensions = this.extensions.filter((e) => !e.isEncrypted());
1237
1353
  const encryptedExtensions = this.extensions.filter((e) => e.isEncrypted());
1238
1354
  const unencryptedPayload = import_safe2.Bytes.concat([
@@ -1264,7 +1380,7 @@ let Security2CCMessageEncapsulation = (() => {
1264
1380
  key = keyAndIV.key;
1265
1381
  iv = keyAndIV.iv;
1266
1382
  }
1267
- const { ciphertext: ciphertextPayload, authTag } = (0, import_core.encryptAES128CCM)(key, iv, plaintextPayload, authData, import_core.SECURITY_S2_AUTH_TAG_LENGTH);
1383
+ const { ciphertext: ciphertextPayload, authTag } = (0, import_core.encryptAES128CCMSync)(key, iv, plaintextPayload, authData, SECURITY_S2_AUTH_TAG_LENGTH);
1268
1384
  this.key = key;
1269
1385
  this.iv = iv;
1270
1386
  this.authData = authData;
@@ -1280,7 +1396,7 @@ let Security2CCMessageEncapsulation = (() => {
1280
1396
  async serializeAsync(ctx) {
1281
1397
  const securityManager = assertSecurityTX(ctx, this.nodeId);
1282
1398
  this.ensureSequenceNumber(securityManager);
1283
- this.maybeAddSPANExtension(ctx, securityManager);
1399
+ await this.maybeAddSPANExtensionAsync(ctx, securityManager);
1284
1400
  const unencryptedExtensions = this.extensions.filter((e) => !e.isEncrypted());
1285
1401
  const encryptedExtensions = this.extensions.filter((e) => e.isEncrypted());
1286
1402
  const unencryptedPayload = import_safe2.Bytes.concat([
@@ -1301,18 +1417,18 @@ let Security2CCMessageEncapsulation = (() => {
1301
1417
  let key;
1302
1418
  let iv;
1303
1419
  if (this.isSinglecast()) {
1304
- iv = securityManager.nextNonce(this.nodeId, true);
1420
+ iv = await securityManager.nextNonceAsync(this.nodeId, true);
1305
1421
  const { keyCCM } = (
1306
1422
  // Prefer the overridden security class if it was given
1307
1423
  this.securityClass != void 0 ? securityManager.getKeysForSecurityClass(this.securityClass) : securityManager.getKeysForNode(this.nodeId)
1308
1424
  );
1309
1425
  key = keyCCM;
1310
1426
  } else {
1311
- const keyAndIV = securityManager.getMulticastKeyAndIV(destinationTag);
1427
+ const keyAndIV = await securityManager.getMulticastKeyAndIVAsync(destinationTag);
1312
1428
  key = keyAndIV.key;
1313
1429
  iv = keyAndIV.iv;
1314
1430
  }
1315
- const { ciphertext: ciphertextPayload, authTag } = (0, import_core.encryptAES128CCM)(key, iv, plaintextPayload, authData, import_core.SECURITY_S2_AUTH_TAG_LENGTH);
1431
+ const { ciphertext: ciphertextPayload, authTag } = await (0, import_core.encryptAES128CCMAsync)(plaintextPayload, key, iv, authData, SECURITY_S2_AUTH_TAG_LENGTH);
1316
1432
  this.key = key;
1317
1433
  this.iv = iv;
1318
1434
  this.authData = authData;
@@ -1327,7 +1443,7 @@ let Security2CCMessageEncapsulation = (() => {
1327
1443
  }
1328
1444
  computeEncapsulationOverhead() {
1329
1445
  const extensionBytes = this.extensions.map((e) => e.computeLength()).reduce((a, b) => a + b, 0);
1330
- return super.computeEncapsulationOverhead() + 2 + import_core.SECURITY_S2_AUTH_TAG_LENGTH + extensionBytes;
1446
+ return super.computeEncapsulationOverhead() + 2 + SECURITY_S2_AUTH_TAG_LENGTH + extensionBytes;
1331
1447
  }
1332
1448
  toLogEntry(ctx) {
1333
1449
  const message = {