UNPKG

mikrosafe

Version:

Encrypt and decrypt your LocalStorage data, simply and securely.

140 lines (138 loc) 3.53 kB
// src/MikroSafe.ts var MikroSafe = class { cryptoKey; salt; /** * @description Creates a new MikroSafe instance with the provided password. */ constructor(password, options = {}) { if (options.salt) { if (typeof options.salt === "string") { const encoder = new TextEncoder(); this.salt = encoder.encode(options.salt); } else this.salt = options.salt; } else { this.salt = new Uint8Array([ 21, 35, 190, 124, 99, 84, 23, 67, 128, 56, 33, 71, 190, 222, 37, 85 ]); } this.cryptoKey = this.generateKey(password); } /** * @description Store an encrypted value in localStorage. */ async setItem(key, value) { try { const valueString = JSON.stringify(value); const encryptedData = await this.encrypt(valueString); localStorage.setItem(key, encryptedData); } catch (error) { console.error("Failed to encrypt and store data:", error); throw new Error("Encryption failed"); } } /** * @description Retrieve and decrypt a value from localStorage. */ async getItem(key) { const encryptedData = localStorage.getItem(key); if (!encryptedData) return null; try { const decryptedString = await this.decrypt(encryptedData); return JSON.parse(decryptedString); } catch (error) { console.error("Failed to decrypt data:", error); return null; } } /** * @description Remove an item from localStorage. */ removeItem(key) { localStorage.removeItem(key); } /** * @description Clear all items from localStorage. */ clear() { localStorage.clear(); } /** * @description Generate a cryptographic key from the password. */ async generateKey(password) { const passwordBuffer = new TextEncoder().encode(password); const keyMaterial = await window.crypto.subtle.importKey( "raw", passwordBuffer, { name: "PBKDF2" }, false, ["deriveBits", "deriveKey"] ); return window.crypto.subtle.deriveKey( { name: "PBKDF2", salt: this.salt, iterations: 1e5, hash: "SHA-256" }, keyMaterial, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"] ); } /** * @description Encrypt a string using AES-GCM. */ async encrypt(plaintext) { const iv = window.crypto.getRandomValues(new Uint8Array(12)); const key = await this.cryptoKey; const dataBuffer = new TextEncoder().encode(plaintext); const encryptedBuffer = await window.crypto.subtle.encrypt( { name: "AES-GCM", iv }, key, dataBuffer ); const result = new Uint8Array(iv.length + encryptedBuffer.byteLength); result.set(iv, 0); result.set(new Uint8Array(encryptedBuffer), iv.length); return btoa(String.fromCharCode(...result)); } /** * @description Decrypt a string using AES-GCM. */ async decrypt(encryptedData) { const dataBuffer = Uint8Array.from( atob(encryptedData), (c) => c.charCodeAt(0) ); const iv = dataBuffer.slice(0, 12); const encryptedBuffer = dataBuffer.slice(12); const key = await this.cryptoKey; const decryptedBuffer = await window.crypto.subtle.decrypt( { name: "AES-GCM", iv }, key, encryptedBuffer ); return new TextDecoder().decode(decryptedBuffer); } }; export { MikroSafe };