@gang-js/core
Version:
a state sharing algorithm
125 lines (124 loc) • 3.92 kB
JavaScript
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));
});
}
}