eufy-security-client 3.6.0 → 3.7.1

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 (74) hide show
  1. package/.prettierignore/342/200/216 +8 -0
  2. package/.prettierrc +11 -0
  3. package/README.md +18 -0
  4. package/a.ts +61 -0
  5. package/build/error.js.map +1 -1
  6. package/build/eufysecurity.d.ts +1 -0
  7. package/build/eufysecurity.js +721 -224
  8. package/build/eufysecurity.js.map +1 -1
  9. package/build/http/api.d.ts +29 -0
  10. package/build/http/api.js +991 -701
  11. package/build/http/api.js.map +1 -1
  12. package/build/http/cache.js.map +1 -1
  13. package/build/http/const.d.ts +6 -1
  14. package/build/http/const.js +2044 -7536
  15. package/build/http/const.js.map +1 -1
  16. package/build/http/device.d.ts +4 -0
  17. package/build/http/device.js +1325 -440
  18. package/build/http/device.js.map +1 -1
  19. package/build/http/error.js.map +1 -1
  20. package/build/http/index.js.map +1 -1
  21. package/build/http/interfaces.d.ts +22 -15
  22. package/build/http/models.d.ts +2 -1
  23. package/build/http/parameter.js +74 -63
  24. package/build/http/parameter.js.map +1 -1
  25. package/build/http/station.d.ts +20 -2
  26. package/build/http/station.js +8639 -3566
  27. package/build/http/station.js.map +1 -1
  28. package/build/http/types.d.ts +12 -0
  29. package/build/http/types.js +297 -155
  30. package/build/http/types.js.map +1 -1
  31. package/build/http/utils.d.ts +16 -6
  32. package/build/http/utils.js +335 -208
  33. package/build/http/utils.js.map +1 -1
  34. package/build/index.js.map +1 -1
  35. package/build/interfaces.d.ts +4 -3
  36. package/build/logging.js +8 -13
  37. package/build/logging.js.map +1 -1
  38. package/build/mqtt/interface.d.ts +2 -2
  39. package/build/mqtt/service.js +12 -3
  40. package/build/mqtt/service.js.map +1 -1
  41. package/build/p2p/ble.js +7 -6
  42. package/build/p2p/ble.js.map +1 -1
  43. package/build/p2p/error.js.map +1 -1
  44. package/build/p2p/interfaces.d.ts +41 -6
  45. package/build/p2p/session.js +1484 -383
  46. package/build/p2p/session.js.map +1 -1
  47. package/build/p2p/talkback.js.map +1 -1
  48. package/build/p2p/types.js +36 -36
  49. package/build/p2p/types.js.map +1 -1
  50. package/build/p2p/utils.d.ts +10 -0
  51. package/build/p2p/utils.js +183 -90
  52. package/build/p2p/utils.js.map +1 -1
  53. package/build/push/client.js +15 -4
  54. package/build/push/client.js.map +1 -1
  55. package/build/push/error.js.map +1 -1
  56. package/build/push/interfaces.d.ts +8 -8
  57. package/build/push/models.js.map +1 -1
  58. package/build/push/parser.js +6 -2
  59. package/build/push/parser.js.map +1 -1
  60. package/build/push/service.js +214 -85
  61. package/build/push/service.js.map +1 -1
  62. package/build/push/types.js.map +1 -1
  63. package/build/push/utils.js.map +1 -1
  64. package/build/utils.js +7 -15
  65. package/build/utils.js.map +1 -1
  66. package/coverage/clover.xml +11133 -13648
  67. package/coverage/coverage-final.json +20 -30
  68. package/coverage/lcov-report/error.ts.html +3 -3
  69. package/coverage/lcov-report/index.html +50 -65
  70. package/coverage/lcov-report/logging.ts.html +598 -0
  71. package/coverage/lcov.info +21072 -25751
  72. package/dont-care.js +101 -0
  73. package/package.json +9 -5
  74. package/build/package.json +0 -81
@@ -36,8 +36,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.getLockP2PCommand = exports.getSmartSafeP2PCommand = exports.decodeSmartSafeData = exports.decodeP2PCloudIPs = exports.buildTalkbackAudioFrameHeader = exports.getLockV12Key = exports.getAdvancedLockKey = exports.eufyKDF = exports.decryptPayloadData = exports.encryptPayloadData = exports.buildVoidCommandPayload = exports.checkT8420 = exports.getVideoCodec = exports.generateAdvancedLockAESKey = exports.eslTimestamp = exports.decodeBase64 = exports.decodeLockPayload = exports.getLockVectorBytes = exports.encodeLockPayload = exports.generateLockSequence = exports.getCurrentTimeInSeconds = exports.generateBasicLockAESKey = exports.encryptLockAESData = exports.decryptLockAESData = exports.isIFrame = exports.findStartCode = exports.decryptAESData = exports.getNewRSAPrivateKey = exports.getRSAPrivateKey = exports.sortP2PMessageParts = exports.buildCommandWithStringTypePayload = exports.buildCommandHeader = exports.hasHeader = exports.sendMessage = exports.buildIntStringCommandPayload = exports.buildStringTypeCommandPayload = exports.buildIntCommandPayload = exports.buildCheckCamPayload2 = exports.buildCheckCamPayload = exports.buildLookupWithKeyPayload3 = exports.buildLookupWithKeyPayload2 = exports.buildLookupWithKeyPayload = exports.paddingP2PData = exports.decryptP2PData = exports.encryptP2PData = exports.getP2PCommandEncryptionKey = exports.isP2PCommandEncrypted = exports.getLocalIpAddress = exports.isPrivateIp = exports.MAGIC_WORD = void 0;
40
- exports.readNullTerminatedBuffer = exports.getSmartLockP2PCommand = exports.generateSmartLockAESKey = exports.getSmartLockCurrentTimeInSeconds = exports.isCharging = exports.isPlugSolarCharging = exports.isSolarCharging = exports.isUsbCharging = exports.getNullTerminatedString = exports.RGBColorToDecimal = exports.DecimalToRGBColor = exports.getLockV12P2PCommand = void 0;
39
+ exports.getSmartSafeP2PCommand = exports.decodeSmartSafeData = exports.decodeP2PCloudIPs = exports.buildTalkbackAudioFrameHeader = exports.decryptP2PKeyECDH = exports.getLockV12Key = exports.getAdvancedLockKey = exports.eufyKDF = exports.decryptPayloadData = exports.encryptPayloadData = exports.buildVoidCommandPayload = exports.checkT8420 = exports.getVideoCodec = exports.generateAdvancedLockAESKey = exports.eslTimestamp = exports.decodeBase64 = exports.decodeLockPayload = exports.getLockVectorBytes = exports.encodeLockPayload = exports.generateLockSequence = exports.getCurrentTimeInSeconds = exports.generateBasicLockAESKey = exports.encryptLockAESData = exports.decryptLockAESData = exports.isIFrame = exports.findStartCode = exports.decryptAESData = exports.getNewRSAPrivateKey = exports.getRSAPrivateKey = exports.sortP2PMessageParts = exports.buildCommandWithStringTypePayload = exports.buildCommandHeader = exports.hasHeader = exports.sendMessage = exports.buildIntStringCommandPayload = exports.buildStringTypeCommandPayload = exports.buildIntCommandPayload = exports.buildCheckCamPayload2 = exports.buildCheckCamPayload = exports.buildLookupWithKeyPayload3 = exports.buildLookupWithKeyPayload2 = exports.buildLookupWithKeyPayload = exports.paddingP2PData = exports.decryptP2PData = exports.encryptP2PData = exports.getP2PCommandEncryptionKey = exports.isP2PCommandEncrypted = exports.getLocalIpAddress = exports.isPrivateIp = exports.MAGIC_WORD = void 0;
40
+ exports.readNullTerminatedBuffer = exports.getSmartLockP2PCommand = exports.generateSmartLockAESKey = exports.getSmartLockCurrentTimeInSeconds = exports.isCharging = exports.isPlugSolarCharging = exports.isSolarCharging = exports.isUsbCharging = exports.getNullTerminatedString = exports.RGBColorToDecimal = exports.DecimalToRGBColor = exports.getLockV12P2PCommand = exports.getLockP2PCommand = void 0;
41
41
  exports.isP2PQueueMessage = isP2PQueueMessage;
42
42
  const node_rsa_1 = __importDefault(require("node-rsa"));
43
43
  const CryptoJS = __importStar(require("crypto-js"));
@@ -60,7 +60,9 @@ const isPrivateIp = (ip) => /^(::f{4}:)?10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1
60
60
  exports.isPrivateIp = isPrivateIp;
61
61
  const stringWithLength = (input, chunkLength = 128) => {
62
62
  const stringAsBuffer = Buffer.from(input);
63
- const bufferSize = stringAsBuffer.byteLength < chunkLength ? chunkLength : Math.ceil(stringAsBuffer.byteLength / chunkLength) * chunkLength;
63
+ const bufferSize = stringAsBuffer.byteLength < chunkLength
64
+ ? chunkLength
65
+ : Math.ceil(stringAsBuffer.byteLength / chunkLength) * chunkLength;
64
66
  const result = Buffer.alloc(bufferSize);
65
67
  stringAsBuffer.copy(result);
66
68
  return result;
@@ -89,7 +91,16 @@ const p2pDidToBuffer = (p2pDid) => {
89
91
  return Buffer.concat([buf1, buf2, buf3], 20);
90
92
  };
91
93
  const isP2PCommandEncrypted = function (cmd) {
92
- return [1001, 1002, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1015, 1017, 1019, 1035, 1045, 1056, 1145, 1146, 1152, 1200, 1207, 1210, 1213, 1214, 1226, 1227, 1229, 1230, 1233, 1236, 1240, 1241, 1243, 1246, 1272, 1273, 1275, 1400, 1401, 1402, 1403, 1408, 1409, 1410, 1412, 1413, 1506, 1507, 1607, 1609, 1610, 1611, 1702, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1013, 1202, 1205, 1206, 1024, 1025, 1132, 1215, 1216, 1217, 1414, 1026, 1164, 1201, 1027, 1047, 1048, 1029, 1034, 1036, 1043, 1057, 1203, 1218, 1219, 1220, 1221, 1222, 1223, 1224, 1232, 1234, 1235, 1237, 1238, 1248, 1253, 1257, 1269, 1800, 1037, 1040, 1038, 1049, 1050, 1051, 1054, 1060, 1204, 1254, 1255, 1256, 1258, 1259, 1260, 1261, 1262, 1264, 1271, 1350, 1404, 1101, 1106, 1108, 1110, 1111, 1112, 1113, 1114, 1116, 1117, 1118, 1119, 1121, 1103, 1129, 1211, 1228, 1231, 1242, 1249, 1250, 1251, 1252, 1405, 1406, 1407, 1700].includes(cmd);
94
+ return [
95
+ 1001, 1002, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1015, 1017, 1019, 1035, 1045, 1056, 1145, 1146, 1152,
96
+ 1200, 1207, 1210, 1213, 1214, 1226, 1227, 1229, 1230, 1233, 1236, 1240, 1241, 1243, 1246, 1272, 1273, 1275, 1400,
97
+ 1401, 1402, 1403, 1408, 1409, 1410, 1412, 1413, 1506, 1507, 1607, 1609, 1610, 1611, 1702, 1703, 1704, 1705, 1706,
98
+ 1707, 1708, 1709, 1013, 1202, 1205, 1206, 1024, 1025, 1132, 1215, 1216, 1217, 1414, 1026, 1164, 1201, 1027, 1047,
99
+ 1048, 1029, 1034, 1036, 1043, 1057, 1203, 1218, 1219, 1220, 1221, 1222, 1223, 1224, 1232, 1234, 1235, 1237, 1238,
100
+ 1248, 1253, 1257, 1269, 1800, 1037, 1040, 1038, 1049, 1050, 1051, 1054, 1060, 1204, 1254, 1255, 1256, 1258, 1259,
101
+ 1260, 1261, 1262, 1264, 1271, 1350, 1404, 1101, 1106, 1108, 1110, 1111, 1112, 1113, 1114, 1116, 1117, 1118, 1119,
102
+ 1121, 1103, 1129, 1211, 1228, 1231, 1242, 1249, 1250, 1251, 1252, 1405, 1406, 1407, 1700,
103
+ ].includes(cmd);
93
104
  };
94
105
  exports.isP2PCommandEncrypted = isP2PCommandEncrypted;
95
106
  const getP2PCommandEncryptionKey = function (serialNumber, p2pDid) {
@@ -99,19 +110,13 @@ exports.getP2PCommandEncryptionKey = getP2PCommandEncryptionKey;
99
110
  const encryptP2PData = (data, key) => {
100
111
  const cipher = (0, crypto_1.createCipheriv)("aes-128-ecb", key, null);
101
112
  cipher.setAutoPadding(false);
102
- return Buffer.concat([
103
- cipher.update(data),
104
- cipher.final()
105
- ]);
113
+ return Buffer.concat([cipher.update(data), cipher.final()]);
106
114
  };
107
115
  exports.encryptP2PData = encryptP2PData;
108
116
  const decryptP2PData = (data, key) => {
109
117
  const decipher = (0, crypto_1.createDecipheriv)("aes-128-ecb", key, null);
110
118
  decipher.setAutoPadding(false);
111
- return Buffer.concat([
112
- decipher.update(data),
113
- decipher.final()
114
- ]);
119
+ return Buffer.concat([decipher.update(data), decipher.final()]);
115
120
  };
116
121
  exports.decryptP2PData = decryptP2PData;
117
122
  const paddingP2PData = (data, blocksize = 16) => {
@@ -130,7 +135,9 @@ const buildLookupWithKeyPayload = (socket, p2pDid, dskKey) => {
130
135
  //const ip = socket.address().address;
131
136
  const ip = (0, exports.getLocalIpAddress)(addressInfo.address);
132
137
  const temp_buff = [];
133
- ip.split(".").reverse().forEach(element => {
138
+ ip.split(".")
139
+ .reverse()
140
+ .forEach((element) => {
134
141
  temp_buff.push(Number.parseInt(element));
135
142
  });
136
143
  const ipAsBuffer = Buffer.from(temp_buff);
@@ -153,7 +160,10 @@ const buildLookupWithKeyPayload3 = (p2pDid, address, data) => {
153
160
  const portAsBuffer = Buffer.allocUnsafe(2);
154
161
  portAsBuffer.writeUInt16LE(address.port, 0);
155
162
  const temp_buff = [];
156
- address.host.split(".").reverse().forEach(element => {
163
+ address.host
164
+ .split(".")
165
+ .reverse()
166
+ .forEach((element) => {
157
167
  temp_buff.push(Number.parseInt(element));
158
168
  });
159
169
  const ipAsBuffer = Buffer.from(temp_buff);
@@ -183,10 +193,7 @@ const buildIntCommandPayload = (encryptionType, encryptionKey, serialNumber, p2p
183
193
  valueBuffer.writeUInt32LE(value, 0);
184
194
  const headerBuffer = Buffer.allocUnsafe(2);
185
195
  const strValueBuffer = strValue.length === 0 ? Buffer.from([]) : stringWithLength(strValue);
186
- const tmpDataBuffer = Buffer.concat([
187
- valueBuffer,
188
- strValueBuffer
189
- ]);
196
+ const tmpDataBuffer = Buffer.concat([valueBuffer, strValueBuffer]);
190
197
  const dataBuffer = encrypted ? (0, exports.paddingP2PData)(tmpDataBuffer) : tmpDataBuffer;
191
198
  headerBuffer.writeUInt16LE(dataBuffer.length, 0);
192
199
  return Buffer.concat([
@@ -195,7 +202,7 @@ const buildIntCommandPayload = (encryptionType, encryptionKey, serialNumber, p2p
195
202
  magicBuffer,
196
203
  channelBuffer,
197
204
  emptyBuffer,
198
- encrypted ? (0, exports.encryptP2PData)(dataBuffer, encryptionKey) : dataBuffer
205
+ encrypted ? (0, exports.encryptP2PData)(dataBuffer, encryptionKey) : dataBuffer,
199
206
  ]);
200
207
  };
201
208
  exports.buildIntCommandPayload = buildIntCommandPayload;
@@ -208,11 +215,7 @@ const buildStringTypeCommandPayload = (encryptionType, encryptionKey, serialNumb
208
215
  const strValueBuffer = stringWithLength(strValue);
209
216
  const strValueSubBuffer = stringWithLength(strValueSub);
210
217
  const headerBuffer = Buffer.allocUnsafe(2);
211
- const tmpDataBuffer = Buffer.concat([
212
- someBuffer,
213
- strValueBuffer,
214
- strValueSubBuffer
215
- ]);
218
+ const tmpDataBuffer = Buffer.concat([someBuffer, strValueBuffer, strValueSubBuffer]);
216
219
  const dataBuffer = encrypted ? (0, exports.paddingP2PData)(tmpDataBuffer) : tmpDataBuffer;
217
220
  headerBuffer.writeUInt16LE(dataBuffer.length, 0);
218
221
  return Buffer.concat([
@@ -221,7 +224,7 @@ const buildStringTypeCommandPayload = (encryptionType, encryptionKey, serialNumb
221
224
  magicBuffer,
222
225
  channelBuffer,
223
226
  emptyBuffer,
224
- encrypted ? (0, exports.encryptP2PData)(dataBuffer, encryptionKey) : dataBuffer
227
+ encrypted ? (0, exports.encryptP2PData)(dataBuffer, encryptionKey) : dataBuffer,
225
228
  ]);
226
229
  };
227
230
  exports.buildStringTypeCommandPayload = buildStringTypeCommandPayload;
@@ -237,12 +240,7 @@ const buildIntStringCommandPayload = (encryptionType, encryptionKey, serialNumbe
237
240
  const strValueBuffer = strValue.length === 0 ? Buffer.from([]) : stringWithLength(strValue);
238
241
  const strValueSubBuffer = strValueSub.length === 0 ? Buffer.from([]) : stringWithLength(strValueSub);
239
242
  const headerBuffer = Buffer.allocUnsafe(2);
240
- const tmpDataBuffer = Buffer.concat([
241
- someintBuffer,
242
- valueBuffer,
243
- strValueBuffer,
244
- strValueSubBuffer
245
- ]);
243
+ const tmpDataBuffer = Buffer.concat([someintBuffer, valueBuffer, strValueBuffer, strValueSubBuffer]);
246
244
  const dataBuffer = encrypted ? (0, exports.paddingP2PData)(tmpDataBuffer) : tmpDataBuffer;
247
245
  headerBuffer.writeUInt16LE(dataBuffer.length, 0);
248
246
  return Buffer.concat([
@@ -251,7 +249,7 @@ const buildIntStringCommandPayload = (encryptionType, encryptionKey, serialNumbe
251
249
  magicBuffer,
252
250
  channelBuffer,
253
251
  emptyBuffer,
254
- encrypted ? (0, exports.encryptP2PData)(dataBuffer, encryptionKey) : dataBuffer
252
+ encrypted ? (0, exports.encryptP2PData)(dataBuffer, encryptionKey) : dataBuffer,
255
253
  ]);
256
254
  };
257
255
  exports.buildIntStringCommandPayload = buildIntStringCommandPayload;
@@ -305,13 +303,14 @@ const buildCommandWithStringTypePayload = (encryptionType, encryptionKey, serial
305
303
  magicBuffer,
306
304
  channelBuffer,
307
305
  emptyBuffer,
308
- encrypted ? (0, exports.encryptP2PData)(dataBuffer, encryptionKey) : dataBuffer
306
+ encrypted ? (0, exports.encryptP2PData)(dataBuffer, encryptionKey) : dataBuffer,
309
307
  ]);
310
308
  };
311
309
  exports.buildCommandWithStringTypePayload = buildCommandWithStringTypePayload;
312
310
  const sortP2PMessageParts = (messages) => {
313
311
  let completeMessage = Buffer.from([]);
314
- Object.keys(messages).map(Number)
312
+ Object.keys(messages)
313
+ .map(Number)
315
314
  .sort((a, b) => {
316
315
  if (Math.abs(a - b) > 65000) {
317
316
  if (a < b) {
@@ -339,7 +338,7 @@ const getRSAPrivateKey = (pem, enableEmbeddedPKCS1Support = false) => {
339
338
  }
340
339
  key.importKey(pem, "pkcs8");
341
340
  const options = {
342
- encryptionScheme: "pkcs1"
341
+ encryptionScheme: "pkcs1",
343
342
  };
344
343
  if (enableEmbeddedPKCS1Support) {
345
344
  options.environment = "browser";
@@ -351,7 +350,7 @@ exports.getRSAPrivateKey = getRSAPrivateKey;
351
350
  const getNewRSAPrivateKey = (enableEmbeddedPKCS1Support = false) => {
352
351
  const key = new node_rsa_1.default({ b: 1024 });
353
352
  const options = {
354
- encryptionScheme: "pkcs1"
353
+ encryptionScheme: "pkcs1",
355
354
  };
356
355
  if (enableEmbeddedPKCS1Support) {
357
356
  options.environment = "browser";
@@ -363,11 +362,11 @@ exports.getNewRSAPrivateKey = getNewRSAPrivateKey;
363
362
  const decryptAESData = (hexkey, data) => {
364
363
  const key = CryptoJS.enc.Hex.parse(hexkey);
365
364
  const cipherParams = CryptoJS.lib.CipherParams.create({
366
- ciphertext: CryptoJS.enc.Hex.parse(data.toString("hex"))
365
+ ciphertext: CryptoJS.enc.Hex.parse(data.toString("hex")),
367
366
  });
368
367
  const decrypted = CryptoJS.AES.decrypt(cipherParams, key, {
369
368
  mode: CryptoJS.mode.ECB,
370
- padding: CryptoJS.pad.NoPadding
369
+ padding: CryptoJS.pad.NoPadding,
371
370
  });
372
371
  return Buffer.from(CryptoJS.enc.Hex.stringify(decrypted), "hex");
373
372
  };
@@ -376,12 +375,13 @@ const findStartCode = (data) => {
376
375
  if (data !== undefined && data.length > 0) {
377
376
  if (data.length >= 4) {
378
377
  const startcode = [...data.subarray(0, 4)];
379
- if ((startcode[0] === 0 && startcode[1] === 0 && startcode[2] === 1) || (startcode[0] === 0 && startcode[1] === 0 && startcode[2] === 0 && startcode[3] === 1))
378
+ if ((startcode[0] === 0 && startcode[1] === 0 && startcode[2] === 1) ||
379
+ (startcode[0] === 0 && startcode[1] === 0 && startcode[2] === 0 && startcode[3] === 1))
380
380
  return true;
381
381
  }
382
382
  else if (data.length === 3) {
383
383
  const startcode = [...data.subarray(0, 3)];
384
- if ((startcode[0] === 0 && startcode[1] === 0 && startcode[2] === 1))
384
+ if (startcode[0] === 0 && startcode[1] === 0 && startcode[2] === 1)
385
385
  return true;
386
386
  }
387
387
  }
@@ -404,12 +404,12 @@ const decryptLockAESData = (key, iv, data) => {
404
404
  const ekey = CryptoJS.enc.Hex.parse(key);
405
405
  const eiv = CryptoJS.enc.Hex.parse(iv);
406
406
  const cipherParams = CryptoJS.lib.CipherParams.create({
407
- ciphertext: CryptoJS.enc.Hex.parse(data.toString("hex"))
407
+ ciphertext: CryptoJS.enc.Hex.parse(data.toString("hex")),
408
408
  });
409
409
  const decrypted = CryptoJS.AES.decrypt(cipherParams, ekey, {
410
410
  iv: eiv,
411
411
  mode: CryptoJS.mode.CBC,
412
- padding: CryptoJS.pad.Pkcs7
412
+ padding: CryptoJS.pad.Pkcs7,
413
413
  });
414
414
  return Buffer.from(CryptoJS.enc.Hex.stringify(decrypted), "hex");
415
415
  };
@@ -420,7 +420,7 @@ const encryptLockAESData = (key, iv, data) => {
420
420
  const encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Hex.parse(data.toString("hex")), ekey, {
421
421
  iv: eiv,
422
422
  mode: CryptoJS.mode.CBC,
423
- padding: CryptoJS.pad.Pkcs7
423
+ padding: CryptoJS.pad.Pkcs7,
424
424
  });
425
425
  return Buffer.from(CryptoJS.enc.Hex.stringify(encrypted.ciphertext), "hex");
426
426
  };
@@ -431,7 +431,8 @@ const generateBasicLockAESKey = (adminID, stationSN) => {
431
431
  const encStationSerial = encoder.encode(stationSN);
432
432
  const array = [104, -83, -72, 38, -107, 99, -110, 17, -95, -121, 54, 57, -46, -98, -111, 89];
433
433
  for (let i = 0; i < 16; i++) {
434
- array[i] = (array[i] + encStationSerial[((encStationSerial[i] * 3) + 5) % 16] + encOwnerID[((encOwnerID[i] * 3) + 5) % 40]);
434
+ array[i] =
435
+ array[i] + encStationSerial[(encStationSerial[i] * 3 + 5) % 16] + encOwnerID[(encOwnerID[i] * 3 + 5) % 40];
435
436
  }
436
437
  return Buffer.from(array).toString("hex");
437
438
  };
@@ -487,7 +488,7 @@ exports.decodeBase64 = decodeBase64;
487
488
  const eslTimestamp = function (timestamp_in_sec = new Date().getTime() / 1000) {
488
489
  const array = [];
489
490
  for (let pos = 0; pos < 4; pos++) {
490
- array[pos] = ((timestamp_in_sec >> (pos * 8)) & 255);
491
+ array[pos] = (timestamp_in_sec >> (pos * 8)) & 255;
491
492
  }
492
493
  return array;
493
494
  };
@@ -520,7 +521,12 @@ const getVideoCodec = (data) => {
520
521
  };
521
522
  exports.getVideoCodec = getVideoCodec;
522
523
  const checkT8420 = (serialNumber) => {
523
- if (!(serialNumber !== undefined && serialNumber !== null && serialNumber.length > 0 && serialNumber.startsWith("T8420")) || serialNumber.length <= 7 || serialNumber[6] != "6") {
524
+ if (!(serialNumber !== undefined &&
525
+ serialNumber !== null &&
526
+ serialNumber.length > 0 &&
527
+ serialNumber.startsWith("T8420")) ||
528
+ serialNumber.length <= 7 ||
529
+ serialNumber[6] != "6") {
524
530
  return false;
525
531
  }
526
532
  return true;
@@ -531,13 +537,7 @@ const buildVoidCommandPayload = (channel = 255) => {
531
537
  const emptyBuffer = Buffer.from([0x00, 0x00]);
532
538
  const magicBuffer = Buffer.from([0x01, 0x00]);
533
539
  const channelBuffer = Buffer.from([channel, 0x00]);
534
- return Buffer.concat([
535
- headerBuffer,
536
- emptyBuffer,
537
- magicBuffer,
538
- channelBuffer,
539
- emptyBuffer
540
- ]);
540
+ return Buffer.concat([headerBuffer, emptyBuffer, magicBuffer, channelBuffer, emptyBuffer]);
541
541
  };
542
542
  exports.buildVoidCommandPayload = buildVoidCommandPayload;
543
543
  function isP2PQueueMessage(type) {
@@ -545,18 +545,12 @@ function isP2PQueueMessage(type) {
545
545
  }
546
546
  const encryptPayloadData = (data, key, iv) => {
547
547
  const cipher = (0, crypto_1.createCipheriv)("aes-128-cbc", key, iv);
548
- return Buffer.concat([
549
- cipher.update(data),
550
- cipher.final()
551
- ]);
548
+ return Buffer.concat([cipher.update(data), cipher.final()]);
552
549
  };
553
550
  exports.encryptPayloadData = encryptPayloadData;
554
551
  const decryptPayloadData = (data, key, iv) => {
555
552
  const cipher = (0, crypto_1.createDecipheriv)("aes-128-cbc", key, iv);
556
- return Buffer.concat([
557
- cipher.update(data),
558
- cipher.final()
559
- ]);
553
+ return Buffer.concat([cipher.update(data), cipher.final()]);
560
554
  };
561
555
  exports.decryptPayloadData = decryptPayloadData;
562
556
  const eufyKDF = (key) => {
@@ -568,7 +562,9 @@ const eufyKDF = (key) => {
568
562
  let tmpBuffer = staticBuffer;
569
563
  for (let step = 0; step < steps; ++step) {
570
564
  tmpBuffer = (0, crypto_1.createHmac)("sha256", key).update(tmpBuffer).digest();
571
- const digest = (0, crypto_1.createHmac)("sha256", key).update(Buffer.concat([tmpBuffer, staticBuffer])).digest();
565
+ const digest = (0, crypto_1.createHmac)("sha256", key)
566
+ .update(Buffer.concat([tmpBuffer, staticBuffer]))
567
+ .digest();
572
568
  digest.copy(buffer, hash_length * step);
573
569
  }
574
570
  return buffer.subarray(0, digest_length);
@@ -585,7 +581,12 @@ const getAdvancedLockKey = (key, publicKey) => {
585
581
  hmac.update(randomValue);
586
582
  hmac.update(encryptedData);
587
583
  const hmacDigest = hmac.digest();
588
- return Buffer.concat([Buffer.from(ecdh.getPublicKey("hex", "compressed"), "hex"), randomValue, encryptedData, hmacDigest]).toString("hex");
584
+ return Buffer.concat([
585
+ Buffer.from(ecdh.getPublicKey("hex", "compressed"), "hex"),
586
+ randomValue,
587
+ encryptedData,
588
+ hmacDigest,
589
+ ]).toString("hex");
589
590
  };
590
591
  exports.getAdvancedLockKey = getAdvancedLockKey;
591
592
  const getLockV12Key = (key, publicKey) => {
@@ -599,9 +600,75 @@ const getLockV12Key = (key, publicKey) => {
599
600
  hmac.update(randomValue);
600
601
  hmac.update(encryptedData);
601
602
  const hmacDigest = hmac.digest();
602
- return Buffer.concat([Buffer.from(ecdh.getPublicKey("hex", "compressed"), "hex"), randomValue, encryptedData, hmacDigest]).toString("hex");
603
+ return Buffer.concat([
604
+ Buffer.from(ecdh.getPublicKey("hex", "compressed"), "hex"),
605
+ randomValue,
606
+ encryptedData,
607
+ hmacDigest,
608
+ ]).toString("hex");
603
609
  };
604
610
  exports.getLockV12Key = getLockV12Key;
611
+ /**
612
+ * Derive P2P session key using ECDH (for devices like E340 that use ECC instead of RSA).
613
+ *
614
+ * The `encryptedKey` buffer from CMD_GATEWAYINFO likely contains:
615
+ * - Device's ECDH public key (either compressed 33 bytes or uncompressed 65 bytes)
616
+ * - Possibly followed by encrypted session key data
617
+ *
618
+ * The `eccPrivateKey` is the 32-byte hex string from the cipher API response.
619
+ */
620
+ const decryptP2PKeyECDH = (encryptedKey, eccPrivateKey) => {
621
+ const ecdh = (0, crypto_1.createECDH)("prime256v1");
622
+ ecdh.setPrivateKey(Buffer.from(eccPrivateKey, "hex"));
623
+ // Determine pubkey format and extract the device's public key
624
+ const firstByte = encryptedKey[0];
625
+ let devicePubKey;
626
+ let pubKeyLen;
627
+ if (firstByte === 0x04) {
628
+ // Uncompressed public key (65 bytes)
629
+ pubKeyLen = 65;
630
+ }
631
+ else if (firstByte === 0x02 || firstByte === 0x03) {
632
+ // Compressed public key (33 bytes)
633
+ pubKeyLen = 33;
634
+ }
635
+ else {
636
+ // Unknown format — try treating first 33 bytes as pubkey
637
+ pubKeyLen = 33;
638
+ }
639
+ // Scenario A/B: encryptedKey IS just the device's public key (no ECIES envelope)
640
+ if (encryptedKey.length <= pubKeyLen + 1) {
641
+ devicePubKey = encryptedKey.subarray(0, pubKeyLen);
642
+ const sharedSecret = ecdh.computeSecret(devicePubKey);
643
+ return sharedSecret.subarray(0, 16);
644
+ }
645
+ // Scenario C: ECIES envelope — pubkey + iv(16) + ciphertext(48) + hmac(32) = 96 bytes after pubkey
646
+ devicePubKey = encryptedKey.subarray(0, pubKeyLen);
647
+ const remainder = encryptedKey.subarray(pubKeyLen);
648
+ const sharedSecret = ecdh.computeSecret(devicePubKey);
649
+ const derivedKey = (0, exports.eufyKDF)(sharedSecret);
650
+ const aesKey = derivedKey.subarray(0, 16);
651
+ // ECIES standard layout: iv(16) + ciphertext(N*16, typically 48) + hmac(32)
652
+ // Minimum valid: iv(16) + ciphertext(16) + hmac(32) = 64 bytes
653
+ if (remainder.length >= 64) {
654
+ const iv = remainder.subarray(0, 16);
655
+ const hmacLen = 32;
656
+ const ciphertext = remainder.subarray(16, remainder.length - hmacLen);
657
+ // Ensure ciphertext length is a multiple of 16 (AES block size)
658
+ const alignedLen = Math.floor(ciphertext.length / 16) * 16;
659
+ if (alignedLen > 0) {
660
+ const alignedCiphertext = ciphertext.subarray(0, alignedLen);
661
+ const decipher = (0, crypto_1.createDecipheriv)("aes-128-cbc", aesKey, iv);
662
+ decipher.setAutoPadding(false);
663
+ const decrypted = Buffer.concat([decipher.update(alignedCiphertext), decipher.final()]);
664
+ // Return first 16 bytes as the P2P session key (AES-128-ECB requires 16-byte key)
665
+ return decrypted.subarray(0, 16);
666
+ }
667
+ }
668
+ // Fallback: if remainder is too small for ECIES, try using raw shared secret
669
+ return sharedSecret.subarray(0, 16);
670
+ };
671
+ exports.decryptP2PKeyECDH = decryptP2PKeyECDH;
605
672
  const buildTalkbackAudioFrameHeader = (audioData, channel = 0) => {
606
673
  const audioDataLength = Buffer.allocUnsafe(4);
607
674
  audioDataLength.writeUInt32LE(audioData.length);
@@ -615,13 +682,7 @@ const buildTalkbackAudioFrameHeader = (audioData, channel = 0) => {
615
682
  const magicBuffer = Buffer.from([0x01, 0x00]);
616
683
  const channelBuffer = Buffer.from([channel, 0x00]);
617
684
  const emptyBuffer = Buffer.from([0x00, 0x00]);
618
- return Buffer.concat([
619
- bytesToRead,
620
- magicBuffer,
621
- channelBuffer,
622
- emptyBuffer,
623
- audioDataHeader
624
- ]);
685
+ return Buffer.concat([bytesToRead, magicBuffer, channelBuffer, emptyBuffer, audioDataHeader]);
625
686
  };
626
687
  exports.buildTalkbackAudioFrameHeader = buildTalkbackAudioFrameHeader;
627
688
  const decodeP2PCloudIPs = (data) => {
@@ -633,12 +694,15 @@ const decodeP2PCloudIPs = (data) => {
633
694
  for (let j = 0; j < i; j++) {
634
695
  z = z ^ output[j];
635
696
  }
636
- const x = (data.charCodeAt(i * 2 + 1) - "A".charCodeAt(0));
697
+ const x = data.charCodeAt(i * 2 + 1) - "A".charCodeAt(0);
637
698
  const y = (data.charCodeAt(i * 2) - "A".charCodeAt(0)) * 0x10;
638
- output[i] = z ^ lookupTable[i % lookupTable.length] ^ x + y;
699
+ output[i] = z ^ lookupTable[i % lookupTable.length] ^ (x + y);
639
700
  }
640
701
  const result = [];
641
- output.toString("utf8").split(",").forEach((ip) => {
702
+ output
703
+ .toString("utf8")
704
+ .split(",")
705
+ .forEach((ip) => {
642
706
  if (ip !== "") {
643
707
  result.push({ host: ip, port: 32100 });
644
708
  }
@@ -654,7 +718,7 @@ const decodeSmartSafeData = function (deviceSN, data) {
654
718
  commandCode: response.getCommandCode(),
655
719
  packageFlag: response.getPackageFlag(),
656
720
  responseCode: response.getResponseCode(),
657
- data: (0, exports.decryptPayloadData)(response.getData(), Buffer.from(deviceSN), Buffer.from(device_1.SmartSafe.IV, "hex"))
721
+ data: (0, exports.decryptPayloadData)(response.getData(), Buffer.from(deviceSN), Buffer.from(device_1.SmartSafe.IV, "hex")),
658
722
  };
659
723
  };
660
724
  exports.decodeSmartSafeData = decodeSmartSafeData;
@@ -666,7 +730,15 @@ const getSmartSafeP2PCommand = function (deviceSN, user_id, command, intCommand,
666
730
  .setDataType(-1)
667
731
  .setData(encPayload)
668
732
  .getSmartSafeCommand();
669
- logging_1.rootP2PLogger.debug(`Generate smart safe command`, { deviceSN: deviceSN, userId: user_id, command: command, intCommand: intCommand, channel: channel, sequence: sequence, data: data.toString("hex") });
733
+ logging_1.rootP2PLogger.debug(`Generate smart safe command`, {
734
+ deviceSN: deviceSN,
735
+ userId: user_id,
736
+ command: command,
737
+ intCommand: intCommand,
738
+ channel: channel,
739
+ sequence: sequence,
740
+ data: data.toString("hex"),
741
+ });
670
742
  return {
671
743
  commandType: types_1.CommandType.CMD_SET_PAYLOAD,
672
744
  value: JSON.stringify({
@@ -678,9 +750,9 @@ const getSmartSafeP2PCommand = function (deviceSN, user_id, command, intCommand,
678
750
  data: bleCommand.toString("hex"),
679
751
  prj_id: command,
680
752
  seq_num: sequence,
681
- }
753
+ },
682
754
  }),
683
- channel: channel
755
+ channel: channel,
684
756
  };
685
757
  };
686
758
  exports.getSmartSafeP2PCommand = getSmartSafeP2PCommand;
@@ -689,7 +761,13 @@ const getLockP2PCommand = function (deviceSN, user_id, command, channel, lockPub
689
761
  const ecdhKey = (0, exports.getAdvancedLockKey)(key, lockPublicKey);
690
762
  const iv = (0, exports.getLockVectorBytes)(deviceSN);
691
763
  const encPayload = (0, exports.encryptLockAESData)(key, iv, Buffer.from(JSON.stringify(payload)));
692
- logging_1.rootP2PLogger.debug(`Generate lock command`, { deviceSN: deviceSN, userId: user_id, command: command, channel: channel, data: JSON.stringify(payload) });
764
+ logging_1.rootP2PLogger.debug(`Generate lock command`, {
765
+ deviceSN: deviceSN,
766
+ userId: user_id,
767
+ command: command,
768
+ channel: channel,
769
+ data: JSON.stringify(payload),
770
+ });
693
771
  return {
694
772
  commandType: types_1.CommandType.CMD_SET_PAYLOAD,
695
773
  value: JSON.stringify({
@@ -698,10 +776,10 @@ const getLockP2PCommand = function (deviceSN, user_id, command, channel, lockPub
698
776
  cmd: command,
699
777
  mChannel: channel,
700
778
  mValue3: 0,
701
- payload: encPayload.toString("base64")
779
+ payload: encPayload.toString("base64"),
702
780
  }).replace(/=/g, "\\u003d"),
703
781
  channel: channel,
704
- aesKey: key
782
+ aesKey: key,
705
783
  };
706
784
  };
707
785
  exports.getLockP2PCommand = getLockP2PCommand;
@@ -710,7 +788,14 @@ const getLockV12P2PCommand = function (deviceSN, user_id, command, channel, lock
710
788
  const encryptedAesKey = (0, exports.getLockV12Key)(key, lockPublicKey);
711
789
  const iv = (0, exports.getLockVectorBytes)(deviceSN);
712
790
  const encPayload = (0, exports.encryptPayloadData)(data, Buffer.from(key, "hex"), Buffer.from(iv, "hex"));
713
- logging_1.rootP2PLogger.debug(`Generate smart lock v12 command`, { deviceSN: deviceSN, userId: user_id, command: command, channel: channel, sequence: sequence, data: data.toString("hex") });
791
+ logging_1.rootP2PLogger.debug(`Generate smart lock v12 command`, {
792
+ deviceSN: deviceSN,
793
+ userId: user_id,
794
+ command: command,
795
+ channel: channel,
796
+ sequence: sequence,
797
+ data: data.toString("hex"),
798
+ });
714
799
  const bleCommand = new ble_1.BleCommandFactory()
715
800
  .setVersionCode(device_1.Lock.VERSION_CODE_LOCKV12)
716
801
  .setCommandCode(Number.parseInt(types_1.ESLBleCommand[types_1.ESLCommand[command]])) //TODO: Change internal command identification?
@@ -731,9 +816,9 @@ const getLockV12P2PCommand = function (deviceSN, user_id, command, channel, lock
731
816
  apiCommand: command,
732
817
  lock_payload: bleCommand.getLockV12Command().toString("hex"),
733
818
  seq_num: sequence,
734
- }
735
- })
736
- }
819
+ },
820
+ }),
821
+ },
737
822
  };
738
823
  };
739
824
  exports.getLockV12P2PCommand = getLockV12P2PCommand;
@@ -746,7 +831,7 @@ const DecimalToRGBColor = function (color) {
746
831
  };
747
832
  exports.DecimalToRGBColor = DecimalToRGBColor;
748
833
  const RGBColorToDecimal = function (color) {
749
- return (color.red << 16) + (color.green << 8) + (color.blue);
834
+ return (color.red << 16) + (color.green << 8) + color.blue;
750
835
  };
751
836
  exports.RGBColorToDecimal = RGBColorToDecimal;
752
837
  const getNullTerminatedString = function (data, encoding) {
@@ -785,7 +870,15 @@ const getSmartLockP2PCommand = function (deviceSN, user_id, command, channel, se
785
870
  const key = (0, exports.generateSmartLockAESKey)(user_id, time);
786
871
  const iv = (0, exports.getLockVectorBytes)(deviceSN);
787
872
  const encPayload = (0, exports.encryptPayloadData)(data, key, Buffer.from(iv, "hex"));
788
- logging_1.rootP2PLogger.debug(`Generate smart lock command`, { deviceSN: deviceSN, userId: user_id, command: command, channel: channel, sequence: sequence, data: data.toString("hex"), functionType: functionType });
873
+ logging_1.rootP2PLogger.debug(`Generate smart lock command`, {
874
+ deviceSN: deviceSN,
875
+ userId: user_id,
876
+ command: command,
877
+ channel: channel,
878
+ sequence: sequence,
879
+ data: data.toString("hex"),
880
+ functionType: functionType,
881
+ });
789
882
  let commandCode = 0;
790
883
  if (functionType === types_1.SmartLockFunctionType.TYPE_1) {
791
884
  commandCode = Number.parseInt(types_1.SmartLockBleCommandFunctionType1[types_1.SmartLockCommand[command]]);
@@ -812,9 +905,9 @@ const getSmartLockP2PCommand = function (deviceSN, user_id, command, channel, se
812
905
  lock_payload: bleCommand.getSmartLockCommand().toString("hex"),
813
906
  seq_num: sequence,
814
907
  time: time,
815
- }
816
- })
817
- }
908
+ },
909
+ }),
910
+ },
818
911
  };
819
912
  };
820
913
  exports.getSmartLockP2PCommand = getSmartLockP2PCommand;