]> git.codecow.com Git - libnemo.git/commitdiff
Refactor safe retrieval to get multiple records.
authorChris Duncan <chris@zoso.dev>
Fri, 18 Jul 2025 16:06:21 +0000 (09:06 -0700)
committerChris Duncan <chris@zoso.dev>
Fri, 18 Jul 2025 16:06:21 +0000 (09:06 -0700)
src/lib/workers/safe.ts

index 59e7a4a071c21be06480956b67c248f249b40cb0..073f9d95ce5817d2a035878fc539c8f8e7214a00 100644 (file)
@@ -76,8 +76,8 @@ export class Safe extends WorkerInterface {
                        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)) {
@@ -102,10 +102,12 @@ export class Safe extends WorkerInterface {
        /**
        * 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')
                }
@@ -113,16 +115,20 @@ export class Safe extends WorkerInterface {
                        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
@@ -161,15 +167,16 @@ export class Safe extends WorkerInterface {
                })
        }
 
-       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)
                        }