]> git.codecow.com Git - libnemo.git/commitdiff
Remove passkey worker since salts are locked up in Safe anyway.
authorChris Duncan <chris@zoso.dev>
Sun, 27 Jul 2025 06:48:06 +0000 (23:48 -0700)
committerChris Duncan <chris@zoso.dev>
Sun, 27 Jul 2025 06:48:06 +0000 (23:48 -0700)
src/lib/account.ts
src/lib/rolodex.ts
src/lib/workers/passkey.ts [deleted file]
src/lib/workers/safe.ts
src/types.d.ts

index 56a2b8acaa45a493f65329d3dd5b8f8c0b61d38e..d4d259f232b5c6677de95a255ef646cf91a7671f 100644 (file)
@@ -79,7 +79,7 @@ export class Account {
                        await SafeWorker.request<boolean>({\r
                                method: 'destroy',\r
                                store: 'Account',\r
-                               [this.publicKey]: this.publicKey\r
+                               name: this.publicKey\r
                        })\r
                } catch (err) {\r
                        console.error(err)\r
index e3efbd5791c254c934d03d30c9ac443214488b48..527be64da3267d74edff2c6af2c958b59062c8b8 100644 (file)
@@ -95,7 +95,7 @@ export class Rolodex {
                const { result } = await SafeWorker.request<boolean>({
                        method: 'destroy',
                        store: 'Rolodex',
-                       [address]: address
+                       name: address
                })
                return result
        }
@@ -109,13 +109,14 @@ export class Rolodex {
        static async deleteName (name: string): Promise<boolean> {
                const data: NamedData = {
                        method: 'destroy',
-                       store: 'Rolodex',
-                       [name]: name
+                       store: 'Rolodex'
                }
+               const names: string[] = [name]
                const addresses = await this.getAddresses(name)
                for (const address of addresses) {
-                       data[address] = address
+                       names.push(address)
                }
+               data.name = JSON.stringify(names)
                const { result } = await SafeWorker.request<boolean>(data)
                return result
        }
diff --git a/src/lib/workers/passkey.ts b/src/lib/workers/passkey.ts
deleted file mode 100644 (file)
index 2082fbe..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
-//! SPDX-License-Identifier: GPL-3.0-or-later
-
-'use strict'
-
-import { WorkerInterface } from './worker-interface'
-import { NamedData } from '#types'
-
-/**
-* Converts a user password to a `CryptoKey`.
-*/
-export class Passkey extends WorkerInterface {
-       static {
-               this.listen()
-       }
-
-       static async work (data: NamedData | unknown): Promise<NamedData<ArrayBuffer | CryptoKey>> {
-               const { purpose, password, salt } = this.#extractData(data)
-               try {
-                       const key = await this.#createAesKey(purpose, password, salt)
-                       return { salt, key }
-               } catch (err) {
-                       throw new Error('Failed to derive key from password', { cause: err })
-               } finally {
-                       password.transfer()
-               }
-       }
-
-       static #extractData (data: unknown) {
-               if (data == null) {
-                       throw new TypeError('Worker received no data')
-               }
-               if (typeof data !== 'object') {
-                       throw new Error('Invalid data')
-               }
-               const dataObject = data as { [key: string]: unknown }
-               if (!('password' in dataObject) || !(dataObject.password instanceof ArrayBuffer)) {
-                       throw new TypeError('Password must be ArrayBuffer')
-               }
-               const password: ArrayBuffer = dataObject.password
-               if (!('purpose' in dataObject)) {
-                       throw new TypeError('Key purpose is required')
-               }
-               if (dataObject.purpose !== 'encrypt' && dataObject.purpose !== 'decrypt') {
-                       throw new TypeError('Invalid key purpose')
-               }
-               const purpose: 'encrypt' | 'decrypt' = dataObject.purpose
-               if (purpose === 'decrypt' && !('salt' in dataObject)) {
-                       throw new TypeError('Salt required for decryption key')
-               }
-               if (dataObject.salt != null && !(dataObject.salt instanceof ArrayBuffer)) {
-                       throw new TypeError('Salt must be ArrayBuffer')
-               }
-               const salt: ArrayBuffer = dataObject.salt ?? globalThis.crypto.getRandomValues(new Uint8Array(32)).buffer
-               return { purpose, password, salt }
-       }
-
-       static async #createAesKey (purpose: 'encrypt' | 'decrypt', password: ArrayBuffer, salt: ArrayBuffer): Promise<CryptoKey> {
-               const derivationKey = await globalThis.crypto.subtle.importKey('raw', password, 'PBKDF2', false, ['deriveBits', 'deriveKey'])
-               const derivationAlgorithm: Pbkdf2Params = {
-                       name: 'PBKDF2',
-                       hash: 'SHA-512',
-                       iterations: 210000,
-                       salt
-               }
-               const derivedKeyType: AesKeyGenParams = {
-                       name: 'AES-GCM',
-                       length: 256
-               }
-               return await globalThis.crypto.subtle.deriveKey(derivationAlgorithm, derivationKey, derivedKeyType, false, [purpose])
-       }
-}
-
-let importWorkerThreads = ''
-NODE: importWorkerThreads = `import { parentPort } from 'node:worker_threads'`
-export default `
-       ${importWorkerThreads}
-       const WorkerInterface = ${WorkerInterface}
-       const Passkey = ${Passkey}
-`
index 591ad8c85a976cbeace931bc27be305e4869822f..86b53bc30c881d220b0398e2fdd208cab60b5bee 100644 (file)
@@ -22,22 +22,29 @@ export class Safe extends WorkerInterface {
                this.listen()
        }
 
-       static async work (data: NamedData): Promise<NamedData<boolean | ArrayBuffer>> {
-               const { method, name, store } = data
+       static async work (data: NamedData | unknown): Promise<NamedData<boolean | ArrayBuffer>> {
+               if (data == null) {
+                       throw new TypeError('Worker received no data')
+               }
+               if (typeof data !== 'object') {
+                       throw new TypeError('Invalid data')
+               }
+               const { method, names, store, password } = data as { [key: string]: unknown }
                if (typeof method !== 'string') {
                        throw new TypeError('Invalid method')
                }
-               delete data.method
-               if (name != null && typeof name !== 'string') {
-                       throw new TypeError('Invalid name')
+               function validateNames (names: unknown): asserts names is string[] {
+                       if (!Array.isArray(names) || names.some(n => typeof n !== 'string')) {
+                               throw new TypeError('Invalid name')
+                       }
                }
-               delete data.name
+               validateNames(names)
                if (typeof store !== 'string') {
                        throw new TypeError('Invalid store')
                }
-               delete data.store
-               const password = data.password
-               delete data.password
+               if (password != null && !(password instanceof ArrayBuffer)) {
+                       throw new TypeError('Invalid password')
+               }
                this.#storage = await this.#open(this.DB_NAME)
                try {
                        switch (method) {
@@ -45,13 +52,13 @@ export class Safe extends WorkerInterface {
                                        return { result: await this.store(data, store, password) }
                                }
                                case 'fetch': {
-                                       return await this.fetch(name, store, password)
+                                       return await this.fetch(names, store, password)
                                }
                                case 'export': {
                                        return await this.export(store, password)
                                }
                                case 'destroy': {
-                                       return { result: await this.destroy(data, store) }
+                                       return { result: await this.destroy(names, store) }
                                }
                                default: {
                                        throw new Error(`unknown Safe method ${method}`)
@@ -66,9 +73,9 @@ export class Safe extends WorkerInterface {
        /**
        * Removes data from the Safe without decrypting.
        */
-       static async destroy (data: NamedData, store: string): Promise<boolean> {
+       static async destroy (names: string[], store: string): Promise<boolean> {
                try {
-                       return await this.#delete(Object.keys(data), store)
+                       return await this.#delete(names, store)
                } catch (err) {
                        console.error(err)
                        throw new Error(this.ERR_MSG)
@@ -113,22 +120,13 @@ export class Safe extends WorkerInterface {
        /**
        * Retrieves data from the Safe and decrypts it with a password byte array.
        */
-       static async fetch (name: string | string[] | unknown, store: string | unknown, password: ArrayBuffer | unknown): Promise<NamedData<ArrayBuffer>> {
-               const names = Array.isArray(name) ? name : [name]
-               if (names.some(v => typeof v !== 'string')) {
-                       throw new Error('Invalid fields')
+       static async fetch (names: string[], store: string, password: ArrayBuffer | unknown): Promise<NamedData<ArrayBuffer>> {
+               if (password == null || !(password instanceof ArrayBuffer)) {
+                       throw new TypeError('Invalid password')
                }
-               const fields: string[] = names
-               if (typeof store !== 'string' || store === '') {
-                       throw new Error('Invalid database store name')
-               }
-               if (!(password instanceof ArrayBuffer)) {
-                       throw new Error('Invalid password')
-               }
-
                const results: NamedData<ArrayBuffer> = {}
                try {
-                       const records: SafeRecord[] = await this.#get(fields, store)
+                       const records: SafeRecord[] = await this.#get(names, store)
                        if (records == null) {
                                throw new Error('')
                        }
index 3bf50bf557e2c936254186ec3e564f5e296e5ef4..a68fc92308f3cdc269b745ada1d68147b8595bcd 100644 (file)
@@ -377,7 +377,7 @@ export declare class ChangeBlock extends Block {
        constructor (account: Account | string, balance: string, representative: Account | string, frontier: string, work?: string)
 }
 
-export type Data = boolean | number[] | string | ArrayBuffer | CryptoKey
+export type Data = boolean | number[] | string | string[] | ArrayBuffer | CryptoKey
 
 /**
 * Represents a cryptographically strong source of entropy suitable for use in