import Store from 'electron-store'; import * as crypto from 'crypto'; import { v4 as uuidv4 } from 'uuid'; // Share 数据结构 export interface ShareEntry { id: string; sessionId: string; walletName: string; partyId: string; partyIndex: number; threshold: { t: number; n: number; }; publicKey: string; encryptedShare: string; createdAt: string; lastUsedAt?: string; metadata: { participants: Array<{ partyId: string; name: string; }>; }; } // 存储的数据结构 interface StoreSchema { version: string; shares: ShareEntry[]; settings: { messageRouterHost: string; messageRouterPort: number; autoBackup: boolean; }; } // 加密配置 const ALGORITHM = 'aes-256-gcm'; const KEY_LENGTH = 32; const IV_LENGTH = 16; const SALT_LENGTH = 32; const TAG_LENGTH = 16; const ITERATIONS = 100000; /** * 安全存储类 - 本地加密存储 share */ export class SecureStorage { private store: Store; constructor() { this.store = new Store({ name: 'service-party-data', defaults: { version: '1.0.0', shares: [], settings: { messageRouterHost: 'localhost', messageRouterPort: 9092, autoBackup: false, }, }, encryptionKey: 'service-party-app-encryption-key', // 基础加密,share 数据还有额外加密 }); } /** * 从密码派生密钥 */ private deriveKey(password: string, salt: Buffer): Buffer { return crypto.pbkdf2Sync(password, salt, ITERATIONS, KEY_LENGTH, 'sha256'); } /** * 加密数据 */ private encrypt(data: string, password: string): string { const salt = crypto.randomBytes(SALT_LENGTH); const key = this.deriveKey(password, salt); const iv = crypto.randomBytes(IV_LENGTH); const cipher = crypto.createCipheriv(ALGORITHM, key, iv); let encrypted = cipher.update(data, 'utf8', 'hex'); encrypted += cipher.final('hex'); const tag = cipher.getAuthTag(); // 格式: salt(hex) + iv(hex) + tag(hex) + encrypted(hex) return salt.toString('hex') + iv.toString('hex') + tag.toString('hex') + encrypted; } /** * 解密数据 */ private decrypt(encryptedData: string, password: string): string { const salt = Buffer.from(encryptedData.slice(0, SALT_LENGTH * 2), 'hex'); const iv = Buffer.from(encryptedData.slice(SALT_LENGTH * 2, SALT_LENGTH * 2 + IV_LENGTH * 2), 'hex'); const tag = Buffer.from(encryptedData.slice(SALT_LENGTH * 2 + IV_LENGTH * 2, SALT_LENGTH * 2 + IV_LENGTH * 2 + TAG_LENGTH * 2), 'hex'); const encrypted = encryptedData.slice(SALT_LENGTH * 2 + IV_LENGTH * 2 + TAG_LENGTH * 2); const key = this.deriveKey(password, salt); const decipher = crypto.createDecipheriv(ALGORITHM, key, iv); decipher.setAuthTag(tag); let decrypted = decipher.update(encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } /** * 保存 share */ saveShare(share: Omit & { rawShare: string }, password: string): ShareEntry { const encryptedShare = this.encrypt(share.rawShare, password); const entry: ShareEntry = { id: uuidv4(), sessionId: share.sessionId, walletName: share.walletName, partyId: share.partyId, partyIndex: share.partyIndex, threshold: share.threshold, publicKey: share.publicKey, encryptedShare, createdAt: new Date().toISOString(), metadata: share.metadata, }; const shares = this.store.get('shares', []); shares.push(entry); this.store.set('shares', shares); return entry; } /** * 获取 share 列表 (不含加密数据) */ listShares(): Omit[] { const shares = this.store.get('shares', []); return shares.map(({ encryptedShare: _, ...rest }) => rest); } /** * 获取单个 share (解密) */ getShare(id: string, password: string): ShareEntry & { rawShare: string } { const shares = this.store.get('shares', []); const share = shares.find((s) => s.id === id); if (!share) { throw new Error('Share not found'); } const rawShare = this.decrypt(share.encryptedShare, password); return { ...share, rawShare, }; } /** * 更新 share 使用时间 */ updateLastUsed(id: string): void { const shares = this.store.get('shares', []); const index = shares.findIndex((s) => s.id === id); if (index !== -1) { shares[index].lastUsedAt = new Date().toISOString(); this.store.set('shares', shares); } } /** * 删除 share */ deleteShare(id: string): void { const shares = this.store.get('shares', []); const filtered = shares.filter((s) => s.id !== id); this.store.set('shares', filtered); } /** * 导出 share (加密后的备份文件) */ exportShare(id: string, password: string): Buffer { const share = this.getShare(id, password); const exportData = { version: '1.0.0', exportedAt: new Date().toISOString(), share: { ...share, // 导出时使用新密码重新加密 encryptedShare: this.encrypt(share.rawShare, password), }, }; // 再次加密整个导出数据 const encryptedExport = this.encrypt(JSON.stringify(exportData), password); return Buffer.from(encryptedExport, 'utf8'); } /** * 导入 share */ importShare(data: Buffer, password: string): ShareEntry { const encryptedExport = data.toString('utf8'); // 解密导出数据 const decrypted = this.decrypt(encryptedExport, password); const exportData = JSON.parse(decrypted); if (!exportData.version || !exportData.share) { throw new Error('Invalid export file format'); } // 解密 share 数据 const rawShare = this.decrypt(exportData.share.encryptedShare, password); // 检查是否已存在相同的 share const shares = this.store.get('shares', []); const existing = shares.find( (s) => s.sessionId === exportData.share.sessionId && s.partyId === exportData.share.partyId ); if (existing) { throw new Error('Share already exists'); } // 保存导入的 share return this.saveShare( { sessionId: exportData.share.sessionId, walletName: exportData.share.walletName, partyId: exportData.share.partyId, partyIndex: exportData.share.partyIndex, threshold: exportData.share.threshold, publicKey: exportData.share.publicKey, metadata: exportData.share.metadata, rawShare, }, password ); } /** * 获取设置 */ getSettings() { return this.store.get('settings'); } /** * 更新设置 */ updateSettings(settings: Partial): void { const current = this.store.get('settings'); this.store.set('settings', { ...current, ...settings }); } /** * 保存设置 (完全替换) */ saveSettings(settings: StoreSchema['settings']): void { this.store.set('settings', settings); } }