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.
- package/.prettierignore/342/200/216 +8 -0
- package/.prettierrc +11 -0
- package/README.md +18 -0
- package/a.ts +61 -0
- package/build/error.js.map +1 -1
- package/build/eufysecurity.d.ts +1 -0
- package/build/eufysecurity.js +721 -224
- package/build/eufysecurity.js.map +1 -1
- package/build/http/api.d.ts +29 -0
- package/build/http/api.js +991 -701
- package/build/http/api.js.map +1 -1
- package/build/http/cache.js.map +1 -1
- package/build/http/const.d.ts +6 -1
- package/build/http/const.js +2044 -7536
- package/build/http/const.js.map +1 -1
- package/build/http/device.d.ts +4 -0
- package/build/http/device.js +1325 -440
- package/build/http/device.js.map +1 -1
- package/build/http/error.js.map +1 -1
- package/build/http/index.js.map +1 -1
- package/build/http/interfaces.d.ts +22 -15
- package/build/http/models.d.ts +2 -1
- package/build/http/parameter.js +74 -63
- package/build/http/parameter.js.map +1 -1
- package/build/http/station.d.ts +20 -2
- package/build/http/station.js +8639 -3566
- package/build/http/station.js.map +1 -1
- package/build/http/types.d.ts +12 -0
- package/build/http/types.js +297 -155
- package/build/http/types.js.map +1 -1
- package/build/http/utils.d.ts +16 -6
- package/build/http/utils.js +335 -208
- package/build/http/utils.js.map +1 -1
- package/build/index.js.map +1 -1
- package/build/interfaces.d.ts +4 -3
- package/build/logging.js +8 -13
- package/build/logging.js.map +1 -1
- package/build/mqtt/interface.d.ts +2 -2
- package/build/mqtt/service.js +12 -3
- package/build/mqtt/service.js.map +1 -1
- package/build/p2p/ble.js +7 -6
- package/build/p2p/ble.js.map +1 -1
- package/build/p2p/error.js.map +1 -1
- package/build/p2p/interfaces.d.ts +41 -6
- package/build/p2p/session.js +1484 -383
- package/build/p2p/session.js.map +1 -1
- package/build/p2p/talkback.js.map +1 -1
- package/build/p2p/types.js +36 -36
- package/build/p2p/types.js.map +1 -1
- package/build/p2p/utils.d.ts +10 -0
- package/build/p2p/utils.js +183 -90
- package/build/p2p/utils.js.map +1 -1
- package/build/push/client.js +15 -4
- package/build/push/client.js.map +1 -1
- package/build/push/error.js.map +1 -1
- package/build/push/interfaces.d.ts +8 -8
- package/build/push/models.js.map +1 -1
- package/build/push/parser.js +6 -2
- package/build/push/parser.js.map +1 -1
- package/build/push/service.js +214 -85
- package/build/push/service.js.map +1 -1
- package/build/push/types.js.map +1 -1
- package/build/push/utils.js.map +1 -1
- package/build/utils.js +7 -15
- package/build/utils.js.map +1 -1
- package/coverage/clover.xml +11133 -13648
- package/coverage/coverage-final.json +20 -30
- package/coverage/lcov-report/error.ts.html +3 -3
- package/coverage/lcov-report/index.html +50 -65
- package/coverage/lcov-report/logging.ts.html +598 -0
- package/coverage/lcov.info +21072 -25751
- package/dont-care.js +101 -0
- package/package.json +9 -5
- package/build/package.json +0 -81
package/build/p2p/utils.js
CHANGED
|
@@ -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.
|
|
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
|
|
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 [
|
|
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(".")
|
|
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
|
|
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)
|
|
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) ||
|
|
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 (
|
|
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] =
|
|
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] = (
|
|
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 &&
|
|
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)
|
|
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([
|
|
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([
|
|
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 =
|
|
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
|
|
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`, {
|
|
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`, {
|
|
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`, {
|
|
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) +
|
|
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`, {
|
|
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;
|