UNPKG

@gang-js/core

Version:

a state sharing algorithm

125 lines (124 loc) 3.92 kB
import { bytesToString, getRandomBytes, stringToBytes } from '../utils'; const onError = (e) => { console.error(`${e}`); throw new Error(`${e}`); }; export class GangVault { constructor(settings) { this.settings = settings; this.keyParameters = { name: 'AES-GCM', length: 256 }; this.getCryptoKey().then(async (cryptoKey) => { this.cryptoKey = cryptoKey; if (!cryptoKey) await this.setCryptoKey(); }); } setSeed(value) { this.seed = value; } execute(mode, func, onError) { const open = indexedDB.open(this.settings.name, 1); open.onupgradeneeded = () => { open.result.createObjectStore(this.settings.store); }; open.onerror = (e) => onError(e); open.onsuccess = () => { const db = open.result; const tx = db.transaction(this.settings.store, mode); const store = tx.objectStore(this.settings.store); func(store); tx.onerror = (e) => onError(e); tx.oncomplete = () => db.close(); }; } async deleteCryptoKey() { await this.delete(this.settings.key); } async getCryptoKey() { return await this.get(this.settings.key); } async setCryptoKey() { try { this.cryptoKey = (await globalThis.crypto.subtle.generateKey(this.keyParameters, false, [ 'encrypt', 'decrypt' ])); await this.set(this.settings.key, this.cryptoKey); return this.cryptoKey; } catch (err) { onError(err); } } get(key, getDefault = null) { return new Promise((resolve, reject) => { this.execute('readonly', async (store) => { const get = store.get(key); get.onsuccess = () => { if (get.result === undefined) { resolve(getDefault && getDefault()); return; } resolve(get.result); }; }, (e) => reject(e)); }); } async getEncrypted(key) { try { const data = await this.get(key); const buffer = await globalThis.crypto.subtle.decrypt({ name: this.keyParameters.name, iv: data.iv, tagLength: 128, additionalData: this.seed }, this.cryptoKey, data.value); return JSON.parse(bytesToString(buffer)); } catch (err) { onError(err); } } set(key, value) { return new Promise((resolve, reject) => { this.execute('readwrite', (store) => { const put = store.put(value, key); put.onsuccess = () => { resolve(); }; }, (e) => reject(e)); }); } async setEncrypted(key, value) { try { const buffer = stringToBytes(JSON.stringify(value)); const iv = getRandomBytes(12); const data = { value: await globalThis.crypto.subtle.encrypt({ name: this.keyParameters.name, iv, tagLength: 128, additionalData: this.seed }, this.cryptoKey, buffer), iv }; await this.set(key, data); } catch (err) { onError(err); } } async delete(key) { return new Promise((resolve, reject) => { this.execute('readwrite', (store) => { const put = store.delete(key); put.onsuccess = () => { resolve(); }; }, (e) => reject(e)); }); } }