qidao-openclaw-plugin 2.0.0-beta.1 → 2.0.2
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/auth-cli.encrypted.js +8 -0
- package/auth-cli.js +1 -0
- package/config.js +1 -0
- package/device-fingerprint.encrypted.js +8 -0
- package/device-fingerprint.js +1 -0
- package/index.js +1 -0
- package/integrity.json +42 -0
- package/package.json +48 -63
- package/plugin.js +1 -0
- package/qidao-channel.js +1 -0
- package/{src/qr-server.js → qr-server.js} +86 -67
- package/src/auth-cli.js +0 -187
- package/src/config.js +0 -116
- package/src/device-fingerprint.js +0 -425
- package/src/index.js +0 -512
- package/src/plugin.js +0 -205
- package/src/qidao-channel.js +0 -513
|
@@ -1,425 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 设备指纹生成和签名模块
|
|
3
|
-
*
|
|
4
|
-
* 功能:
|
|
5
|
-
* 1. 生成基于硬件信息的设备指纹
|
|
6
|
-
* 2. 计算请求签名
|
|
7
|
-
* 3. 管理设备盐
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import crypto from 'crypto';
|
|
11
|
-
import os from 'os';
|
|
12
|
-
import fs from 'fs';
|
|
13
|
-
import path from 'path';
|
|
14
|
-
|
|
15
|
-
export class DeviceFingerprint {
|
|
16
|
-
constructor() {
|
|
17
|
-
this.saltFile = path.join(os.homedir(), '.openclaw', '.device-salt');
|
|
18
|
-
this.salt = this.loadOrCreateSalt();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 加载或创建设备盐(企业级验证)
|
|
23
|
-
* @returns {string} 设备盐
|
|
24
|
-
*/
|
|
25
|
-
loadOrCreateSalt() {
|
|
26
|
-
// 1. 生成真实的硬件特征盐
|
|
27
|
-
const realSalt = this.generateHardwareBasedSalt();
|
|
28
|
-
|
|
29
|
-
// 2. 检查持久化文件
|
|
30
|
-
let storedSalt = null;
|
|
31
|
-
try {
|
|
32
|
-
if (fs.existsSync(this.saltFile)) {
|
|
33
|
-
storedSalt = fs.readFileSync(this.saltFile, 'utf8').trim();
|
|
34
|
-
}
|
|
35
|
-
} catch (error) {
|
|
36
|
-
// 静默处理读取失败
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// 3. 企业级验证:如果文件存在,验证其真实性
|
|
40
|
-
if (storedSalt) {
|
|
41
|
-
if (this.validateSalt(storedSalt, realSalt)) {
|
|
42
|
-
return storedSalt;
|
|
43
|
-
} else {
|
|
44
|
-
// 删除被篡改的文件
|
|
45
|
-
try {
|
|
46
|
-
fs.unlinkSync(this.saltFile);
|
|
47
|
-
} catch (error) {
|
|
48
|
-
// 静默处理删除失败
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// 4. 保存真实的设备盐
|
|
54
|
-
this.saveSalt(realSalt);
|
|
55
|
-
return realSalt;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* 生成基于硬件特征的设备盐
|
|
60
|
-
* @returns {string} 硬件设备盐
|
|
61
|
-
*/
|
|
62
|
-
generateHardwareBasedSalt() {
|
|
63
|
-
const saltComponents = [];
|
|
64
|
-
|
|
65
|
-
// 1. 主机名
|
|
66
|
-
saltComponents.push(os.hostname());
|
|
67
|
-
|
|
68
|
-
// 2. 平台和架构
|
|
69
|
-
saltComponents.push(os.platform());
|
|
70
|
-
saltComponents.push(os.arch());
|
|
71
|
-
|
|
72
|
-
// 3. CPU信息
|
|
73
|
-
try {
|
|
74
|
-
const cpus = os.cpus();
|
|
75
|
-
if (cpus && cpus.length > 0) {
|
|
76
|
-
saltComponents.push(cpus[0].model);
|
|
77
|
-
saltComponents.push(cpus.length.toString());
|
|
78
|
-
}
|
|
79
|
-
} catch (error) {
|
|
80
|
-
// 静默处理
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// 4. 内存信息
|
|
84
|
-
try {
|
|
85
|
-
saltComponents.push(os.totalmem().toString());
|
|
86
|
-
} catch (error) {
|
|
87
|
-
// 静默处理
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// 5. 网络接口MAC地址
|
|
91
|
-
try {
|
|
92
|
-
const macAddresses = this.getMacAddresses();
|
|
93
|
-
if (macAddresses.length > 0) {
|
|
94
|
-
saltComponents.push(macAddresses.join(','));
|
|
95
|
-
}
|
|
96
|
-
} catch (error) {
|
|
97
|
-
// 静默处理
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// 6. 用户目录
|
|
101
|
-
saltComponents.push(os.homedir());
|
|
102
|
-
|
|
103
|
-
// 7. 盐标识符
|
|
104
|
-
saltComponents.push('QIDAO_DEVICE_SALT_V1');
|
|
105
|
-
|
|
106
|
-
const salt = crypto
|
|
107
|
-
.createHash('sha256')
|
|
108
|
-
.update(saltComponents.join('|'))
|
|
109
|
-
.digest('hex');
|
|
110
|
-
|
|
111
|
-
return salt;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* 验证设备盐的真实性(防篡改)
|
|
116
|
-
* @param {string} storedSalt 存储的设备盐
|
|
117
|
-
* @param {string} realSalt 真实的设备盐
|
|
118
|
-
* @returns {boolean} 是否有效
|
|
119
|
-
*/
|
|
120
|
-
validateSalt(storedSalt, realSalt) {
|
|
121
|
-
return storedSalt === realSalt;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* 保存设备盐到持久化文件
|
|
126
|
-
* @param {string} salt 设备盐
|
|
127
|
-
*/
|
|
128
|
-
saveSalt(salt) {
|
|
129
|
-
try {
|
|
130
|
-
const dir = path.dirname(this.saltFile);
|
|
131
|
-
if (!fs.existsSync(dir)) {
|
|
132
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
133
|
-
}
|
|
134
|
-
fs.writeFileSync(this.saltFile, salt, { mode: 0o600 });
|
|
135
|
-
} catch (error) {
|
|
136
|
-
// 静默处理保存失败
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* 获取机器唯一ID(企业级验证)
|
|
142
|
-
* @returns {string} 机器ID
|
|
143
|
-
*/
|
|
144
|
-
getMachineId() {
|
|
145
|
-
const machineIdFile = path.join(os.homedir(), '.openclaw', '.machine-id');
|
|
146
|
-
|
|
147
|
-
// 1. 首先尝试使用node-machine-id获取真实硬件ID
|
|
148
|
-
let realMachineId = null;
|
|
149
|
-
try {
|
|
150
|
-
const { machineIdSync } = require('node-machine-id');
|
|
151
|
-
realMachineId = machineIdSync();
|
|
152
|
-
} catch (error) {
|
|
153
|
-
// 静默处理,使用备用方案
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// 2. 如果没有真实硬件ID,生成基于硬件特征的ID
|
|
157
|
-
if (!realMachineId) {
|
|
158
|
-
realMachineId = this.generateHardwareBasedId();
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// 3. 检查持久化文件
|
|
162
|
-
let storedMachineId = null;
|
|
163
|
-
try {
|
|
164
|
-
if (fs.existsSync(machineIdFile)) {
|
|
165
|
-
storedMachineId = fs.readFileSync(machineIdFile, 'utf8').trim();
|
|
166
|
-
}
|
|
167
|
-
} catch (error) {
|
|
168
|
-
// 静默处理读取失败
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// 4. 企业级验证:如果文件存在,验证其真实性
|
|
172
|
-
if (storedMachineId) {
|
|
173
|
-
if (this.validateMachineId(storedMachineId, realMachineId)) {
|
|
174
|
-
return storedMachineId;
|
|
175
|
-
} else {
|
|
176
|
-
// 删除被篡改的文件
|
|
177
|
-
try {
|
|
178
|
-
fs.unlinkSync(machineIdFile);
|
|
179
|
-
} catch (error) {
|
|
180
|
-
// 静默处理删除失败
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// 5. 生成新的机器ID并保存
|
|
186
|
-
this.saveMachineId(realMachineId);
|
|
187
|
-
return realMachineId;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* 生成基于硬件特征的机器ID
|
|
192
|
-
* @returns {string} 硬件机器ID
|
|
193
|
-
*/
|
|
194
|
-
generateHardwareBasedId() {
|
|
195
|
-
const components = [];
|
|
196
|
-
|
|
197
|
-
// 1. 主机名
|
|
198
|
-
components.push(os.hostname());
|
|
199
|
-
|
|
200
|
-
// 2. 平台和架构
|
|
201
|
-
components.push(os.platform());
|
|
202
|
-
components.push(os.arch());
|
|
203
|
-
|
|
204
|
-
// 3. CPU信息(最稳定的硬件特征)
|
|
205
|
-
try {
|
|
206
|
-
const cpus = os.cpus();
|
|
207
|
-
if (cpus && cpus.length > 0) {
|
|
208
|
-
components.push(cpus[0].model);
|
|
209
|
-
components.push(cpus.length.toString());
|
|
210
|
-
}
|
|
211
|
-
} catch (error) {
|
|
212
|
-
// 静默处理
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// 4. 内存信息
|
|
216
|
-
try {
|
|
217
|
-
components.push(os.totalmem().toString());
|
|
218
|
-
} catch (error) {
|
|
219
|
-
// 静默处理
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// 5. 网络接口MAC地址(关键硬件特征)
|
|
223
|
-
try {
|
|
224
|
-
const macAddresses = this.getMacAddresses();
|
|
225
|
-
if (macAddresses.length > 0) {
|
|
226
|
-
components.push(macAddresses.join(','));
|
|
227
|
-
}
|
|
228
|
-
} catch (error) {
|
|
229
|
-
// 静默处理
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// 6. 用户目录
|
|
233
|
-
components.push(os.homedir());
|
|
234
|
-
|
|
235
|
-
// 7. 版本标识
|
|
236
|
-
components.push('QIDAO_HARDWARE_ID_V1');
|
|
237
|
-
|
|
238
|
-
const hardwareId = crypto
|
|
239
|
-
.createHash('sha256')
|
|
240
|
-
.update(components.join('|'))
|
|
241
|
-
.digest('hex');
|
|
242
|
-
|
|
243
|
-
return hardwareId;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* 验证机器ID的真实性(防篡改)
|
|
248
|
-
* @param {string} storedId 存储的机器ID
|
|
249
|
-
* @param {string} realId 真实的机器ID
|
|
250
|
-
* @returns {boolean} 是否有效
|
|
251
|
-
*/
|
|
252
|
-
validateMachineId(storedId, realId) {
|
|
253
|
-
return storedId === realId;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* 保存机器ID到持久化文件
|
|
259
|
-
* @param {string} machineId 机器ID
|
|
260
|
-
*/
|
|
261
|
-
saveMachineId(machineId) {
|
|
262
|
-
const machineIdFile = path.join(os.homedir(), '.openclaw', '.machine-id');
|
|
263
|
-
|
|
264
|
-
try {
|
|
265
|
-
const dir = path.dirname(machineIdFile);
|
|
266
|
-
if (!fs.existsSync(dir)) {
|
|
267
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
268
|
-
}
|
|
269
|
-
fs.writeFileSync(machineIdFile, machineId, { mode: 0o600 });
|
|
270
|
-
} catch (error) {
|
|
271
|
-
// 静默处理保存失败
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* 获取网络接口MAC地址
|
|
277
|
-
* @returns {string[]} MAC地址数组
|
|
278
|
-
*/
|
|
279
|
-
getMacAddresses() {
|
|
280
|
-
const macAddresses = [];
|
|
281
|
-
const networkInterfaces = os.networkInterfaces();
|
|
282
|
-
|
|
283
|
-
Object.values(networkInterfaces).forEach(interfaces => {
|
|
284
|
-
interfaces.forEach(iface => {
|
|
285
|
-
if (iface.mac && iface.mac !== '00:00:00:00:00:00') {
|
|
286
|
-
macAddresses.push(iface.mac);
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// 去重并排序,确保一致性
|
|
292
|
-
return [...new Set(macAddresses)].sort();
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* 生成设备指纹(企业级安全)
|
|
297
|
-
* @returns {string} 设备指纹
|
|
298
|
-
*/
|
|
299
|
-
generate() {
|
|
300
|
-
const components = [];
|
|
301
|
-
|
|
302
|
-
// 1. 机器唯一ID(经过企业级验证)
|
|
303
|
-
try {
|
|
304
|
-
const machineId = this.getMachineId();
|
|
305
|
-
components.push(`machine:${machineId}`);
|
|
306
|
-
} catch (error) {
|
|
307
|
-
throw new Error('设备指纹生成失败:无法获取机器ID');
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// 2. 网络接口MAC地址(硬件特征)
|
|
311
|
-
try {
|
|
312
|
-
const macAddresses = this.getMacAddresses();
|
|
313
|
-
if (macAddresses.length > 0) {
|
|
314
|
-
components.push(`mac:${macAddresses.join(',')}`);
|
|
315
|
-
}
|
|
316
|
-
} catch (error) {
|
|
317
|
-
// 静默处理
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// 3. 主机名
|
|
321
|
-
components.push(`hostname:${os.hostname()}`);
|
|
322
|
-
|
|
323
|
-
// 4. 操作系统信息
|
|
324
|
-
components.push(`platform:${os.platform()}`);
|
|
325
|
-
components.push(`arch:${os.arch()}`);
|
|
326
|
-
|
|
327
|
-
// 5. CPU信息(硬件特征)
|
|
328
|
-
try {
|
|
329
|
-
const cpus = os.cpus();
|
|
330
|
-
if (cpus && cpus.length > 0) {
|
|
331
|
-
components.push(`cpu:${cpus[0].model}`);
|
|
332
|
-
components.push(`cpu_count:${cpus.length}`);
|
|
333
|
-
}
|
|
334
|
-
} catch (error) {
|
|
335
|
-
// 静默处理
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// 6. 内存信息(硬件特征)
|
|
339
|
-
try {
|
|
340
|
-
const totalMem = os.totalmem();
|
|
341
|
-
components.push(`memory:${totalMem}`);
|
|
342
|
-
} catch (error) {
|
|
343
|
-
// 静默处理
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// 7. 用户目录(用户特征)
|
|
347
|
-
try {
|
|
348
|
-
components.push(`homedir:${os.homedir()}`);
|
|
349
|
-
} catch (error) {
|
|
350
|
-
// 静默处理
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// 8. 设备盐(经过企业级验证)
|
|
354
|
-
components.push(`salt:${this.salt}`);
|
|
355
|
-
|
|
356
|
-
// 9. 添加指纹版本标识
|
|
357
|
-
components.push('QIDAO_FINGERPRINT_V1');
|
|
358
|
-
|
|
359
|
-
// 生成指纹(完全确定性,无随机因素)
|
|
360
|
-
const fingerprint = crypto
|
|
361
|
-
.createHash('sha256')
|
|
362
|
-
.update(components.join('|'))
|
|
363
|
-
.digest('hex');
|
|
364
|
-
|
|
365
|
-
// 最终验证:确保指纹不为空
|
|
366
|
-
if (!fingerprint || fingerprint.length !== 64) {
|
|
367
|
-
throw new Error('设备指纹生成失败:指纹格式无效');
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
return fingerprint;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* 计算请求签名
|
|
375
|
-
* @param {string} data 要签名的数据
|
|
376
|
-
* @param {string} fingerprint 设备指纹
|
|
377
|
-
* @returns {string} HMAC签名
|
|
378
|
-
*/
|
|
379
|
-
sign(data, fingerprint) {
|
|
380
|
-
const signature = crypto
|
|
381
|
-
.createHmac('sha256', fingerprint)
|
|
382
|
-
.update(data)
|
|
383
|
-
.digest('hex');
|
|
384
|
-
|
|
385
|
-
return signature;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* 验证签名(用于本地测试)
|
|
390
|
-
* @param {string} data 原始数据
|
|
391
|
-
* @param {string} signature 签名
|
|
392
|
-
* @param {string} fingerprint 设备指纹
|
|
393
|
-
* @returns {boolean} 验证结果
|
|
394
|
-
*/
|
|
395
|
-
verify(data, signature, fingerprint) {
|
|
396
|
-
const expectedSignature = this.sign(data, fingerprint);
|
|
397
|
-
return signature === expectedSignature;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* 获取设备信息摘要(用于调试)
|
|
402
|
-
* @returns {object} 设备信息
|
|
403
|
-
*/
|
|
404
|
-
getDeviceInfo() {
|
|
405
|
-
const machineIdFile = path.join(os.homedir(), '.openclaw', '.machine-id');
|
|
406
|
-
|
|
407
|
-
return {
|
|
408
|
-
hostname: os.hostname(),
|
|
409
|
-
platform: os.platform(),
|
|
410
|
-
arch: os.arch(),
|
|
411
|
-
machineId: this.getMachineId().substring(0, 16) + '...',
|
|
412
|
-
macAddresses: this.getMacAddresses(),
|
|
413
|
-
totalMemory: os.totalmem(),
|
|
414
|
-
cpuCount: os.cpus().length,
|
|
415
|
-
cpuModel: os.cpus()[0]?.model || 'unknown',
|
|
416
|
-
homeDir: os.homedir(),
|
|
417
|
-
saltFile: this.saltFile,
|
|
418
|
-
saltExists: fs.existsSync(this.saltFile),
|
|
419
|
-
machineIdFile: machineIdFile,
|
|
420
|
-
machineIdExists: fs.existsSync(machineIdFile)
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
export default DeviceFingerprint;
|