mikrosafe
Version:
Encrypt and decrypt your LocalStorage data, simply and securely.
140 lines (138 loc) • 3.53 kB
JavaScript
// 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
};