hop-claude 1.0.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.
- package/CHANGELOG.md +316 -0
- package/README.md +574 -0
- package/SECURITY.md +280 -0
- package/bin/cli.js +6 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +147 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/config-manager.d.ts +88 -0
- package/dist/config/config-manager.d.ts.map +1 -0
- package/dist/config/config-manager.js +334 -0
- package/dist/config/config-manager.js.map +1 -0
- package/dist/config/encryption-v2.d.ts +51 -0
- package/dist/config/encryption-v2.d.ts.map +1 -0
- package/dist/config/encryption-v2.js +93 -0
- package/dist/config/encryption-v2.js.map +1 -0
- package/dist/config/encryption.d.ts +36 -0
- package/dist/config/encryption.d.ts.map +1 -0
- package/dist/config/encryption.js +72 -0
- package/dist/config/encryption.js.map +1 -0
- package/dist/config/keychain.d.ts +56 -0
- package/dist/config/keychain.d.ts.map +1 -0
- package/dist/config/keychain.js +112 -0
- package/dist/config/keychain.js.map +1 -0
- package/dist/config/storage.d.ts +32 -0
- package/dist/config/storage.d.ts.map +1 -0
- package/dist/config/storage.js +87 -0
- package/dist/config/storage.js.map +1 -0
- package/dist/config/validator.d.ts +9 -0
- package/dist/config/validator.d.ts.map +1 -0
- package/dist/config/validator.js +53 -0
- package/dist/config/validator.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +38 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/display.d.ts +25 -0
- package/dist/ui/display.d.ts.map +1 -0
- package/dist/ui/display.js +39 -0
- package/dist/ui/display.js.map +1 -0
- package/dist/ui/prompts.d.ts +52 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +339 -0
- package/dist/ui/prompts.js.map +1 -0
- package/dist/utils/backup.d.ts +14 -0
- package/dist/utils/backup.d.ts.map +1 -0
- package/dist/utils/backup.js +27 -0
- package/dist/utils/backup.js.map +1 -0
- package/dist/utils/claude-launcher.d.ts +8 -0
- package/dist/utils/claude-launcher.d.ts.map +1 -0
- package/dist/utils/claude-launcher.js +68 -0
- package/dist/utils/claude-launcher.js.map +1 -0
- package/dist/utils/migration.d.ts +18 -0
- package/dist/utils/migration.d.ts.map +1 -0
- package/dist/utils/migration.js +176 -0
- package/dist/utils/migration.js.map +1 -0
- package/dist/utils/platform.d.ts +19 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +91 -0
- package/dist/utils/platform.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { ConfigStorage } from './storage.js';
|
|
2
|
+
import { Encryption } from './encryption.js';
|
|
3
|
+
import { PassphraseEncryption } from './encryption-v2.js';
|
|
4
|
+
import { KeychainManager } from './keychain.js';
|
|
5
|
+
/**
|
|
6
|
+
* 配置管理核心类
|
|
7
|
+
* 支持三种加密模式:
|
|
8
|
+
* - legacy: 机器绑定加密(向后兼容)
|
|
9
|
+
* - keychain: OS 密钥链存储(推荐)
|
|
10
|
+
* - passphrase: 用户密码加密(可移植)
|
|
11
|
+
*/
|
|
12
|
+
export class ConfigManager {
|
|
13
|
+
storage;
|
|
14
|
+
legacyEncryption;
|
|
15
|
+
passphraseEncryption;
|
|
16
|
+
keychainManager;
|
|
17
|
+
// 用于临时存储用户输入的 passphrase(仅在当前会话有效)
|
|
18
|
+
sessionPassphrase;
|
|
19
|
+
constructor() {
|
|
20
|
+
this.storage = new ConfigStorage();
|
|
21
|
+
this.legacyEncryption = new Encryption();
|
|
22
|
+
this.passphraseEncryption = new PassphraseEncryption();
|
|
23
|
+
this.keychainManager = new KeychainManager();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 设置会话密码(用于 passphrase 模式)
|
|
27
|
+
*/
|
|
28
|
+
setSessionPassphrase(passphrase) {
|
|
29
|
+
this.sessionPassphrase = passphrase;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 清除会话密码
|
|
33
|
+
*/
|
|
34
|
+
clearSessionPassphrase() {
|
|
35
|
+
this.sessionPassphrase = undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 获取当前加密模式
|
|
39
|
+
*/
|
|
40
|
+
async getEncryptionMode() {
|
|
41
|
+
const config = await this.storage.read();
|
|
42
|
+
return config?.encryptionMode || 'legacy'; // 默认 legacy 保持向后兼容
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 初始化配置文件
|
|
46
|
+
*/
|
|
47
|
+
async initialize() {
|
|
48
|
+
const existing = await this.storage.read();
|
|
49
|
+
if (existing) {
|
|
50
|
+
// 确保有 encryptionMode 字段(向后兼容)
|
|
51
|
+
if (!existing.encryptionMode) {
|
|
52
|
+
existing.encryptionMode = 'legacy';
|
|
53
|
+
}
|
|
54
|
+
return existing;
|
|
55
|
+
}
|
|
56
|
+
const newConfig = {
|
|
57
|
+
version: '1.0.0',
|
|
58
|
+
currentProfile: '',
|
|
59
|
+
profiles: [],
|
|
60
|
+
encryptionSalt: this.legacyEncryption.generateSalt(),
|
|
61
|
+
encryptionMode: 'legacy', // 默认使用 legacy 模式
|
|
62
|
+
};
|
|
63
|
+
await this.storage.write(newConfig);
|
|
64
|
+
return newConfig;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 获取所有配置
|
|
68
|
+
*/
|
|
69
|
+
async getConfig() {
|
|
70
|
+
const config = await this.storage.read();
|
|
71
|
+
if (!config)
|
|
72
|
+
return await this.initialize();
|
|
73
|
+
// 向后兼容:添加默认的 encryptionMode
|
|
74
|
+
if (!config.encryptionMode) {
|
|
75
|
+
config.encryptionMode = 'legacy';
|
|
76
|
+
}
|
|
77
|
+
return config;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 添加或更新 profile
|
|
81
|
+
* @param profile 解密后的 profile
|
|
82
|
+
* @param passphrase 密码(仅 passphrase 模式需要)
|
|
83
|
+
*/
|
|
84
|
+
async saveProfile(profile, passphrase) {
|
|
85
|
+
const config = await this.getConfig();
|
|
86
|
+
const mode = config.encryptionMode || 'legacy';
|
|
87
|
+
if (!config.encryptionSalt && mode !== 'keychain') {
|
|
88
|
+
config.encryptionSalt = this.legacyEncryption.generateSalt();
|
|
89
|
+
}
|
|
90
|
+
let encryptedApiKey;
|
|
91
|
+
// 根据加密模式处理 API Key
|
|
92
|
+
switch (mode) {
|
|
93
|
+
case 'keychain':
|
|
94
|
+
// Keychain 模式:存储到 OS 密钥链
|
|
95
|
+
await this.keychainManager.setAPIKey(profile.domain, profile.apiKey);
|
|
96
|
+
// config.json 中存储占位符
|
|
97
|
+
encryptedApiKey = '__KEYCHAIN__';
|
|
98
|
+
break;
|
|
99
|
+
case 'passphrase':
|
|
100
|
+
// Passphrase 模式:使用用户密码加密
|
|
101
|
+
const pwd = passphrase || this.sessionPassphrase;
|
|
102
|
+
if (!pwd) {
|
|
103
|
+
throw new Error('Passphrase required for passphrase encryption mode');
|
|
104
|
+
}
|
|
105
|
+
if (!config.encryptionSalt) {
|
|
106
|
+
config.encryptionSalt = this.passphraseEncryption.generateSalt();
|
|
107
|
+
}
|
|
108
|
+
encryptedApiKey = this.passphraseEncryption.encrypt(profile.apiKey, pwd, config.encryptionSalt);
|
|
109
|
+
break;
|
|
110
|
+
case 'legacy':
|
|
111
|
+
default:
|
|
112
|
+
// Legacy 模式:机器绑定加密
|
|
113
|
+
if (!config.encryptionSalt) {
|
|
114
|
+
config.encryptionSalt = this.legacyEncryption.generateSalt();
|
|
115
|
+
}
|
|
116
|
+
encryptedApiKey = this.legacyEncryption.encrypt(profile.apiKey, config.encryptionSalt);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
const encryptedProfile = {
|
|
120
|
+
...profile,
|
|
121
|
+
apiKey: encryptedApiKey,
|
|
122
|
+
updatedAt: Date.now(),
|
|
123
|
+
};
|
|
124
|
+
const index = config.profiles.findIndex(p => p.domain === profile.domain);
|
|
125
|
+
if (index >= 0) {
|
|
126
|
+
config.profiles[index] = encryptedProfile;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
encryptedProfile.createdAt = Date.now();
|
|
130
|
+
config.profiles.push(encryptedProfile);
|
|
131
|
+
}
|
|
132
|
+
await this.storage.write(config);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* 获取解密后的 profile
|
|
136
|
+
* @param domain profile 名称
|
|
137
|
+
* @param passphrase 密码(仅 passphrase 模式需要)
|
|
138
|
+
*/
|
|
139
|
+
async getProfile(domain, passphrase) {
|
|
140
|
+
const config = await this.getConfig();
|
|
141
|
+
const profile = config.profiles.find(p => p.domain === domain);
|
|
142
|
+
if (!profile)
|
|
143
|
+
return null;
|
|
144
|
+
const mode = config.encryptionMode || 'legacy';
|
|
145
|
+
let decryptedApiKey;
|
|
146
|
+
// 根据加密模式解密 API Key
|
|
147
|
+
switch (mode) {
|
|
148
|
+
case 'keychain':
|
|
149
|
+
// Keychain 模式:从 OS 密钥链读取
|
|
150
|
+
const keychainKey = await this.keychainManager.getAPIKey(domain);
|
|
151
|
+
if (!keychainKey) {
|
|
152
|
+
throw new Error(`API key not found in keychain for profile: ${domain}`);
|
|
153
|
+
}
|
|
154
|
+
decryptedApiKey = keychainKey;
|
|
155
|
+
break;
|
|
156
|
+
case 'passphrase':
|
|
157
|
+
// Passphrase 模式:使用用户密码解密
|
|
158
|
+
const pwd = passphrase || this.sessionPassphrase;
|
|
159
|
+
if (!pwd) {
|
|
160
|
+
throw new Error('Passphrase required to decrypt API key');
|
|
161
|
+
}
|
|
162
|
+
if (!config.encryptionSalt) {
|
|
163
|
+
throw new Error('Encryption salt not found');
|
|
164
|
+
}
|
|
165
|
+
decryptedApiKey = this.passphraseEncryption.decrypt(profile.apiKey, pwd, config.encryptionSalt);
|
|
166
|
+
break;
|
|
167
|
+
case 'legacy':
|
|
168
|
+
default:
|
|
169
|
+
// Legacy 模式:机器绑定解密
|
|
170
|
+
if (!config.encryptionSalt) {
|
|
171
|
+
throw new Error('Encryption salt not found');
|
|
172
|
+
}
|
|
173
|
+
decryptedApiKey = this.legacyEncryption.decrypt(profile.apiKey, config.encryptionSalt);
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
...profile,
|
|
178
|
+
apiKey: decryptedApiKey,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 设置当前激活的 profile
|
|
183
|
+
*/
|
|
184
|
+
async setCurrentProfile(domain) {
|
|
185
|
+
const config = await this.getConfig();
|
|
186
|
+
config.currentProfile = domain;
|
|
187
|
+
await this.storage.write(config);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* 获取当前激活的 profile
|
|
191
|
+
* @param passphrase 密码(仅 passphrase 模式需要)
|
|
192
|
+
*/
|
|
193
|
+
async getCurrentProfile(passphrase) {
|
|
194
|
+
const config = await this.getConfig();
|
|
195
|
+
if (!config.currentProfile)
|
|
196
|
+
return null;
|
|
197
|
+
return await this.getProfile(config.currentProfile, passphrase);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* 列出所有 profiles(API Key 部分隐藏)
|
|
201
|
+
* @param passphrase 密码(仅 passphrase 模式需要)
|
|
202
|
+
*/
|
|
203
|
+
async listProfiles(passphrase) {
|
|
204
|
+
const config = await this.getConfig();
|
|
205
|
+
const mode = config.encryptionMode || 'legacy';
|
|
206
|
+
const results = [];
|
|
207
|
+
for (const p of config.profiles) {
|
|
208
|
+
try {
|
|
209
|
+
let decryptedKey;
|
|
210
|
+
switch (mode) {
|
|
211
|
+
case 'keychain':
|
|
212
|
+
const keychainKey = await this.keychainManager.getAPIKey(p.domain);
|
|
213
|
+
decryptedKey = keychainKey || '';
|
|
214
|
+
break;
|
|
215
|
+
case 'passphrase':
|
|
216
|
+
const pwd = passphrase || this.sessionPassphrase;
|
|
217
|
+
if (!pwd || !config.encryptionSalt) {
|
|
218
|
+
decryptedKey = '';
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
decryptedKey = this.passphraseEncryption.decrypt(p.apiKey, pwd, config.encryptionSalt);
|
|
222
|
+
break;
|
|
223
|
+
case 'legacy':
|
|
224
|
+
default:
|
|
225
|
+
if (!config.encryptionSalt) {
|
|
226
|
+
decryptedKey = '';
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
decryptedKey = this.legacyEncryption.decrypt(p.apiKey, config.encryptionSalt);
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
results.push({
|
|
233
|
+
...p,
|
|
234
|
+
maskedApiKey: this.legacyEncryption.maskApiKey(decryptedKey),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
// 如果解密失败,显示错误标记
|
|
239
|
+
results.push({
|
|
240
|
+
...p,
|
|
241
|
+
maskedApiKey: '[DECRYPT ERROR]',
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return results;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* 删除 profile
|
|
249
|
+
*/
|
|
250
|
+
async deleteProfile(domain) {
|
|
251
|
+
const config = await this.getConfig();
|
|
252
|
+
const mode = config.encryptionMode || 'legacy';
|
|
253
|
+
// Keychain 模式下,同时从密钥链中删除
|
|
254
|
+
if (mode === 'keychain') {
|
|
255
|
+
await this.keychainManager.deleteAPIKey(domain);
|
|
256
|
+
}
|
|
257
|
+
config.profiles = config.profiles.filter(p => p.domain !== domain);
|
|
258
|
+
if (config.currentProfile === domain) {
|
|
259
|
+
config.currentProfile = config.profiles[0]?.domain || '';
|
|
260
|
+
}
|
|
261
|
+
await this.storage.write(config);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* 导出配置(包含加密的数据)
|
|
265
|
+
* 注意:keychain 模式无法导出实际密钥
|
|
266
|
+
*/
|
|
267
|
+
async exportConfig() {
|
|
268
|
+
const config = await this.getConfig();
|
|
269
|
+
// 如果是 keychain 模式,警告用户
|
|
270
|
+
if (config.encryptionMode === 'keychain') {
|
|
271
|
+
console.warn('Warning: Keychain mode cannot export actual API keys. Keys will need to be re-entered after import.');
|
|
272
|
+
}
|
|
273
|
+
return JSON.stringify(config, null, 2);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* 导入配置
|
|
277
|
+
*/
|
|
278
|
+
async importConfig(data) {
|
|
279
|
+
const config = JSON.parse(data);
|
|
280
|
+
// 验证配置格式
|
|
281
|
+
if (!config.version || !Array.isArray(config.profiles)) {
|
|
282
|
+
throw new Error('Invalid configuration format');
|
|
283
|
+
}
|
|
284
|
+
// 向后兼容:如果没有 encryptionMode,设置为 legacy
|
|
285
|
+
if (!config.encryptionMode) {
|
|
286
|
+
config.encryptionMode = 'legacy';
|
|
287
|
+
}
|
|
288
|
+
await this.storage.write(config);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* 切换加密模式(需要提供所有必要的凭据)
|
|
292
|
+
* @param newMode 新的加密模式
|
|
293
|
+
* @param passphrase 密码(切换到 passphrase 模式时需要)
|
|
294
|
+
*/
|
|
295
|
+
async switchEncryptionMode(newMode, passphrase) {
|
|
296
|
+
const config = await this.getConfig();
|
|
297
|
+
const oldMode = config.encryptionMode || 'legacy';
|
|
298
|
+
if (oldMode === newMode) {
|
|
299
|
+
return; // 无需切换
|
|
300
|
+
}
|
|
301
|
+
// 读取所有 profiles 的明文 API Keys
|
|
302
|
+
const decryptedProfiles = [];
|
|
303
|
+
for (const p of config.profiles) {
|
|
304
|
+
const decrypted = await this.getProfile(p.domain, passphrase);
|
|
305
|
+
if (decrypted) {
|
|
306
|
+
decryptedProfiles.push(decrypted);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// 更新加密模式
|
|
310
|
+
config.encryptionMode = newMode;
|
|
311
|
+
// 如果切换到非 legacy/passphrase 模式,可能需要新的 salt
|
|
312
|
+
if (newMode === 'passphrase' && !config.encryptionSalt) {
|
|
313
|
+
config.encryptionSalt = this.passphraseEncryption.generateSalt();
|
|
314
|
+
}
|
|
315
|
+
// 清空旧的 profiles
|
|
316
|
+
config.profiles = [];
|
|
317
|
+
await this.storage.write(config);
|
|
318
|
+
// 使用新模式重新保存所有 profiles
|
|
319
|
+
for (const profile of decryptedProfiles) {
|
|
320
|
+
await this.saveProfile(profile, passphrase);
|
|
321
|
+
}
|
|
322
|
+
// 如果从 keychain 切换出去,清理密钥链
|
|
323
|
+
if (oldMode === 'keychain') {
|
|
324
|
+
await this.keychainManager.clearAll();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* 获取配置文件路径
|
|
329
|
+
*/
|
|
330
|
+
getConfigPath() {
|
|
331
|
+
return this.storage.getConfigPath();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
//# sourceMappingURL=config-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../../src/config/config-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PassphraseEncryption:基于用户密码的加密
|
|
3
|
+
* 用于可移植的配置导出/导入
|
|
4
|
+
* 不依赖机器信息,可以在不同机器间迁移
|
|
5
|
+
*/
|
|
6
|
+
export declare class PassphraseEncryption {
|
|
7
|
+
private algorithm;
|
|
8
|
+
private iterations;
|
|
9
|
+
/**
|
|
10
|
+
* 从用户密码派生加密密钥
|
|
11
|
+
* @param passphrase 用户提供的密码
|
|
12
|
+
* @param salt 盐值
|
|
13
|
+
* @returns 派生的密钥
|
|
14
|
+
*/
|
|
15
|
+
private deriveKey;
|
|
16
|
+
/**
|
|
17
|
+
* 生成随机 salt
|
|
18
|
+
*/
|
|
19
|
+
generateSalt(): string;
|
|
20
|
+
/**
|
|
21
|
+
* 加密明文
|
|
22
|
+
* @param plaintext 要加密的明文
|
|
23
|
+
* @param passphrase 用户密码
|
|
24
|
+
* @param salt 盐值
|
|
25
|
+
* @returns 加密后的密文,格式:iv:authTag:encrypted
|
|
26
|
+
*/
|
|
27
|
+
encrypt(plaintext: string, passphrase: string, salt: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* 解密密文
|
|
30
|
+
* @param ciphertext 密文,格式:iv:authTag:encrypted
|
|
31
|
+
* @param passphrase 用户密码
|
|
32
|
+
* @param salt 盐值
|
|
33
|
+
* @returns 解密后的明文
|
|
34
|
+
*/
|
|
35
|
+
decrypt(ciphertext: string, passphrase: string, salt: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* 验证密码是否正确
|
|
38
|
+
* @param ciphertext 密文
|
|
39
|
+
* @param passphrase 用户密码
|
|
40
|
+
* @param salt 盐值
|
|
41
|
+
* @returns 密码是否正确
|
|
42
|
+
*/
|
|
43
|
+
verifyPassphrase(ciphertext: string, passphrase: string, salt: string): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* 部分隐藏 API Key(用于显示)
|
|
46
|
+
* @param apiKey API Key
|
|
47
|
+
* @returns 部分隐藏的 API Key,如:sk-ant-***xyz
|
|
48
|
+
*/
|
|
49
|
+
maskApiKey(apiKey: string): string;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=encryption-v2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption-v2.d.ts","sourceRoot":"","sources":["../../src/config/encryption-v2.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,UAAU,CAAU;IAE5B;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAIjB;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;;OAMG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAcpE;;;;;;OAMG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAqBrE;;;;;;OAMG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAS/E;;;;OAIG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;CAOnC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
/**
|
|
3
|
+
* PassphraseEncryption:基于用户密码的加密
|
|
4
|
+
* 用于可移植的配置导出/导入
|
|
5
|
+
* 不依赖机器信息,可以在不同机器间迁移
|
|
6
|
+
*/
|
|
7
|
+
export class PassphraseEncryption {
|
|
8
|
+
algorithm = 'aes-256-gcm';
|
|
9
|
+
iterations = 100000; // PBKDF2 迭代次数
|
|
10
|
+
/**
|
|
11
|
+
* 从用户密码派生加密密钥
|
|
12
|
+
* @param passphrase 用户提供的密码
|
|
13
|
+
* @param salt 盐值
|
|
14
|
+
* @returns 派生的密钥
|
|
15
|
+
*/
|
|
16
|
+
deriveKey(passphrase, salt) {
|
|
17
|
+
return crypto.pbkdf2Sync(passphrase, salt, this.iterations, 32, 'sha512');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 生成随机 salt
|
|
21
|
+
*/
|
|
22
|
+
generateSalt() {
|
|
23
|
+
return crypto.randomBytes(32).toString('hex');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 加密明文
|
|
27
|
+
* @param plaintext 要加密的明文
|
|
28
|
+
* @param passphrase 用户密码
|
|
29
|
+
* @param salt 盐值
|
|
30
|
+
* @returns 加密后的密文,格式:iv:authTag:encrypted
|
|
31
|
+
*/
|
|
32
|
+
encrypt(plaintext, passphrase, salt) {
|
|
33
|
+
const key = this.deriveKey(passphrase, salt);
|
|
34
|
+
const iv = crypto.randomBytes(16);
|
|
35
|
+
const cipher = crypto.createCipheriv(this.algorithm, key, iv);
|
|
36
|
+
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
|
|
37
|
+
encrypted += cipher.final('hex');
|
|
38
|
+
const authTag = cipher.getAuthTag();
|
|
39
|
+
// 格式: iv:authTag:encrypted
|
|
40
|
+
return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 解密密文
|
|
44
|
+
* @param ciphertext 密文,格式:iv:authTag:encrypted
|
|
45
|
+
* @param passphrase 用户密码
|
|
46
|
+
* @param salt 盐值
|
|
47
|
+
* @returns 解密后的明文
|
|
48
|
+
*/
|
|
49
|
+
decrypt(ciphertext, passphrase, salt) {
|
|
50
|
+
const key = this.deriveKey(passphrase, salt);
|
|
51
|
+
const parts = ciphertext.split(':');
|
|
52
|
+
if (parts.length !== 3) {
|
|
53
|
+
throw new Error('Invalid encrypted data format');
|
|
54
|
+
}
|
|
55
|
+
const [ivHex, authTagHex, encrypted] = parts;
|
|
56
|
+
const iv = Buffer.from(ivHex, 'hex');
|
|
57
|
+
const authTag = Buffer.from(authTagHex, 'hex');
|
|
58
|
+
const decipher = crypto.createDecipheriv(this.algorithm, key, iv);
|
|
59
|
+
decipher.setAuthTag(authTag);
|
|
60
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
61
|
+
decrypted += decipher.final('utf8');
|
|
62
|
+
return decrypted;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 验证密码是否正确
|
|
66
|
+
* @param ciphertext 密文
|
|
67
|
+
* @param passphrase 用户密码
|
|
68
|
+
* @param salt 盐值
|
|
69
|
+
* @returns 密码是否正确
|
|
70
|
+
*/
|
|
71
|
+
verifyPassphrase(ciphertext, passphrase, salt) {
|
|
72
|
+
try {
|
|
73
|
+
this.decrypt(ciphertext, passphrase, salt);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 部分隐藏 API Key(用于显示)
|
|
82
|
+
* @param apiKey API Key
|
|
83
|
+
* @returns 部分隐藏的 API Key,如:sk-ant-***xyz
|
|
84
|
+
*/
|
|
85
|
+
maskApiKey(apiKey) {
|
|
86
|
+
if (!apiKey || apiKey.length < 8)
|
|
87
|
+
return '***';
|
|
88
|
+
const prefix = apiKey.slice(0, 6);
|
|
89
|
+
const suffix = apiKey.slice(-3);
|
|
90
|
+
return `${prefix}***${suffix}`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=encryption-v2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption-v2.js","sourceRoot":"","sources":["../../src/config/encryption-v2.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 加密类:使用 AES-256-GCM 对称加密
|
|
3
|
+
* 密钥派生:基于机器标识(hostname + username)+ 随机盐
|
|
4
|
+
*/
|
|
5
|
+
export declare class Encryption {
|
|
6
|
+
private algorithm;
|
|
7
|
+
/**
|
|
8
|
+
* 生成加密密钥(基于机器信息 + salt)
|
|
9
|
+
*/
|
|
10
|
+
private deriveKey;
|
|
11
|
+
/**
|
|
12
|
+
* 生成随机 salt
|
|
13
|
+
*/
|
|
14
|
+
generateSalt(): string;
|
|
15
|
+
/**
|
|
16
|
+
* 加密明文
|
|
17
|
+
* @param plaintext 要加密的明文
|
|
18
|
+
* @param salt 盐值
|
|
19
|
+
* @returns 加密后的密文,格式:iv:authTag:encrypted
|
|
20
|
+
*/
|
|
21
|
+
encrypt(plaintext: string, salt: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* 解密密文
|
|
24
|
+
* @param ciphertext 密文,格式:iv:authTag:encrypted
|
|
25
|
+
* @param salt 盐值
|
|
26
|
+
* @returns 解密后的明文
|
|
27
|
+
*/
|
|
28
|
+
decrypt(ciphertext: string, salt: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* 部分隐藏 API Key(用于显示)
|
|
31
|
+
* @param apiKey API Key
|
|
32
|
+
* @returns 部分隐藏的 API Key,如:sk-ant-***xyz
|
|
33
|
+
*/
|
|
34
|
+
maskApiKey(apiKey: string): string;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=encryption.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../src/config/encryption.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAiB;IAElC;;OAEG;IACH,OAAO,CAAC,SAAS;IAKjB;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;OAKG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAchD;;;;;OAKG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAqBjD;;;;OAIG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;CAOnC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
/**
|
|
4
|
+
* 加密类:使用 AES-256-GCM 对称加密
|
|
5
|
+
* 密钥派生:基于机器标识(hostname + username)+ 随机盐
|
|
6
|
+
*/
|
|
7
|
+
export class Encryption {
|
|
8
|
+
algorithm = 'aes-256-gcm';
|
|
9
|
+
/**
|
|
10
|
+
* 生成加密密钥(基于机器信息 + salt)
|
|
11
|
+
*/
|
|
12
|
+
deriveKey(salt) {
|
|
13
|
+
const machineId = `${os.hostname()}-${os.userInfo().username}`;
|
|
14
|
+
return crypto.pbkdf2Sync(machineId, salt, 100000, 32, 'sha512');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 生成随机 salt
|
|
18
|
+
*/
|
|
19
|
+
generateSalt() {
|
|
20
|
+
return crypto.randomBytes(32).toString('hex');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 加密明文
|
|
24
|
+
* @param plaintext 要加密的明文
|
|
25
|
+
* @param salt 盐值
|
|
26
|
+
* @returns 加密后的密文,格式:iv:authTag:encrypted
|
|
27
|
+
*/
|
|
28
|
+
encrypt(plaintext, salt) {
|
|
29
|
+
const key = this.deriveKey(salt);
|
|
30
|
+
const iv = crypto.randomBytes(16);
|
|
31
|
+
const cipher = crypto.createCipheriv(this.algorithm, key, iv);
|
|
32
|
+
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
|
|
33
|
+
encrypted += cipher.final('hex');
|
|
34
|
+
const authTag = cipher.getAuthTag();
|
|
35
|
+
// 格式: iv:authTag:encrypted
|
|
36
|
+
return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 解密密文
|
|
40
|
+
* @param ciphertext 密文,格式:iv:authTag:encrypted
|
|
41
|
+
* @param salt 盐值
|
|
42
|
+
* @returns 解密后的明文
|
|
43
|
+
*/
|
|
44
|
+
decrypt(ciphertext, salt) {
|
|
45
|
+
const key = this.deriveKey(salt);
|
|
46
|
+
const parts = ciphertext.split(':');
|
|
47
|
+
if (parts.length !== 3) {
|
|
48
|
+
throw new Error('Invalid encrypted data format');
|
|
49
|
+
}
|
|
50
|
+
const [ivHex, authTagHex, encrypted] = parts;
|
|
51
|
+
const iv = Buffer.from(ivHex, 'hex');
|
|
52
|
+
const authTag = Buffer.from(authTagHex, 'hex');
|
|
53
|
+
const decipher = crypto.createDecipheriv(this.algorithm, key, iv);
|
|
54
|
+
decipher.setAuthTag(authTag);
|
|
55
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
56
|
+
decrypted += decipher.final('utf8');
|
|
57
|
+
return decrypted;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 部分隐藏 API Key(用于显示)
|
|
61
|
+
* @param apiKey API Key
|
|
62
|
+
* @returns 部分隐藏的 API Key,如:sk-ant-***xyz
|
|
63
|
+
*/
|
|
64
|
+
maskApiKey(apiKey) {
|
|
65
|
+
if (!apiKey || apiKey.length < 8)
|
|
66
|
+
return '***';
|
|
67
|
+
const prefix = apiKey.slice(0, 6);
|
|
68
|
+
const suffix = apiKey.slice(-3);
|
|
69
|
+
return `${prefix}***${suffix}`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=encryption.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.js","sourceRoot":"","sources":["../../src/config/encryption.ts"],"names":[],"mappings":"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"}
|