throw new Error('Invalid password')
}
+ const records: SafeRecord[] = []
try {
- const records: SafeRecord[] = []
const salt = await Entropy.create()
const encryptionKey = await this.#createAesKey('encrypt', password, salt.buffer)
for (const label of Object.keys(data)) {
/**
* Retrieves data from the Safe and decrypts it with a password byte array.
*/
- static async get (name: string | unknown, store: string | unknown, password: ArrayBuffer | unknown): Promise<Data | null> {
- if (typeof name !== 'string' || name === '') {
- throw new Error('Invalid database field name')
+ static async get (name: string | string[] | unknown, store: string | unknown, password: ArrayBuffer | unknown): Promise<Data | null> {
+ const names = Array.isArray(name) ? name : [name]
+ if (names.some(v => typeof v !== 'string')) {
+ throw new Error('Invalid fields')
}
+ const fields: string[] = names
if (typeof store !== 'string' || store === '') {
throw new Error('Invalid database store name')
}
throw new Error('Invalid password')
}
+ const results: Data = {}
try {
- const record: SafeRecord = await this.#get(name, store)
- if (record == null) {
- throw new Error('Failed to find record')
+ const records: SafeRecord[] = await this.#get(fields, store)
+ if (records == null || records.length === 0) {
+ throw new Error('Failed to find records')
}
- const salt = await Entropy.import(record.salt)
- const decryptionKey = await this.#createAesKey('decrypt', password, salt.buffer)
- const iv = await Entropy.import(record.iv)
- const decrypted = await globalThis.crypto.subtle.decrypt({ name: 'AES-GCM', iv: iv.buffer }, decryptionKey, record.encrypted)
- return { [record.label]: decrypted }
+ const decryptionKeys: { [salt: string]: CryptoKey } = {}
+ for (const record of records) {
+ decryptionKeys[record.salt] ??= await this.#createAesKey('decrypt', password, (await Entropy.import(record.salt)).buffer)
+ const iv = await Entropy.import(record.iv)
+ const decrypted = await globalThis.crypto.subtle.decrypt({ name: 'AES-GCM', iv: iv.buffer }, decryptionKeys[record.salt], record.encrypted)
+ results[record.label] = decrypted
+ }
+ return results
} catch (err) {
console.log(err)
return null
})
}
- static async #get (name: string, store: string): Promise<SafeRecord> {
+ static async #get (fields: string[], store: string): Promise<SafeRecord[]> {
const transaction = this.#storage.transaction(store, 'readonly')
const db = transaction.objectStore(store)
return new Promise((resolve, reject) => {
- const request = db.get(name)
- request.onsuccess = (event) => {
- resolve((event.target as IDBRequest).result)
+ const requests = fields.map(field => db.get(field))
+ transaction.oncomplete = (event) => {
+ const results = requests.map(r => r.result)
+ resolve(results)
}
- request.onerror = (event) => {
+ transaction.onerror = (event) => {
console.error('Database error')
reject((event.target as IDBRequest).error)
}