import { EncryptedMindScopeBackup, MindScopeBackup } from '@/lib/assessment-types'; const ITERATIONS = 250_000; function toBase64(bytes: Uint8Array) { let binary = ''; bytes.forEach((byte) => { binary += String.fromCharCode(byte); }); return btoa(binary); } function fromBase64(value: string) { const binary = atob(value); return Uint8Array.from(binary, (character) => character.charCodeAt(0)); } function toArrayBuffer(bytes: Uint8Array): ArrayBuffer { const copy = new Uint8Array(bytes.byteLength); copy.set(bytes); return copy.buffer; } async function deriveKey(password: string, salt: Uint8Array, iterations: number) { const material = await crypto.subtle.importKey( 'raw', new TextEncoder().encode(password), 'PBKDF2', false, ['deriveKey'], ); return crypto.subtle.deriveKey( { name: 'PBKDF2', hash: 'SHA-256', salt: toArrayBuffer(salt), iterations }, material, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt'], ); } export async function encryptBackup(backup: MindScopeBackup, password: string): Promise { if (password.length < 8) throw new Error('备份密码至少需要 8 个字符'); const salt = crypto.getRandomValues(new Uint8Array(16)); const iv = crypto.getRandomValues(new Uint8Array(12)); const key = await deriveKey(password, salt, ITERATIONS); const encrypted = await crypto.subtle.encrypt( { name: 'AES-GCM', iv: toArrayBuffer(iv) }, key, new TextEncoder().encode(JSON.stringify(backup)), ); return { format: 'mindscope-encrypted-backup', version: 1, algorithm: 'AES-GCM', iterations: ITERATIONS, salt: toBase64(salt), iv: toBase64(iv), data: toBase64(new Uint8Array(encrypted)), }; } export async function decryptBackup(backup: EncryptedMindScopeBackup, password: string): Promise { if (backup.format !== 'mindscope-encrypted-backup' || backup.version !== 1) { throw new Error('不支持的加密备份格式'); } try { const salt = fromBase64(backup.salt); const iv = fromBase64(backup.iv); const key = await deriveKey(password, salt, backup.iterations); const decrypted = await crypto.subtle.decrypt( { name: 'AES-GCM', iv: toArrayBuffer(iv) }, key, toArrayBuffer(fromBase64(backup.data)), ); return JSON.parse(new TextDecoder().decode(decrypted)) as MindScopeBackup; } catch { throw new Error('密码错误或备份文件已损坏'); } }