export default class EncryptionHandler { constructor(key, salt){ this._key = key; } static async create(password, salt){ const key = await this.generateEncryptionKey(password, salt); return new EncryptionHandler(key, salt); } static async hashPassword(password, salt){ const iterations = 100000; const hash = "SHA-256"; const encoder = new TextEncoder(); const baseKey = await crypto.subtle.importKey( "raw", encoder.encode(password), {name: "PBKDF2"}, false, ["deriveBits"] ); const derivedBits = await crypto.subtle.deriveBits( { name: "PBKDF2", salt: encoder.encode(salt), iterations, hash }, baseKey, 256 ); return btoa(String.fromCharCode(...new Uint8Array(derivedBits))); } static generateSalt(){ const length = 16; const salt = new Uint8Array(length); crypto.getRandomValues(salt); return btoa(String.fromCharCode(...salt)); } static generateIv(){ const length = 12; const iv = new Uint8Array(length); crypto.getRandomValues(iv); return btoa(String.fromCharCode(...iv)); } stringToBuffer(str){ return Uint8Array.from(atob(str), c => c.charCodeAt(0)); } async generateEncryptionKey(password, salt){ const passwordKey = await crypto.subtle.importKey( "raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveKey"] ); return crypto.subtle.deriveKey( { name: "PBKDF2", salt: salt, iterations: 100000, hash: "SHA-256" }, passwordKey, { name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"] ); } async encrypt(data, iv){ const json = JSON.stringify(data); const buff = new TextEncoder().encode(str); const encrypted = await crypto.subtle.encrypt( { name: "AES-GCM", iv }, this._key, data ); return btoa(String.fromCharCode(...new Uint8Array(encrypted))); } async decrypt(data, key, ivString){ const encrypted = stringToBuffer(data); const iv = stringToBuffer(ivString); const decrypted = await crypto.subtle.decrypt( { name: "AES-GCM", iv, }, key, encrypted ); const json = new TextDecoder().decode(decrypted); return JSON.parse(json); } }