]> zoso.dev Git - libnemo.git/commitdiff
Refactor passkey function to immediately execute polyfill if needed and export the...
authorChris Duncan <chris@zoso.dev>
Mon, 18 Nov 2024 20:15:01 +0000 (12:15 -0800)
committerChris Duncan <chris@zoso.dev>
Mon, 18 Nov 2024 20:15:01 +0000 (12:15 -0800)
package.json
src/lib/passkey.ts
src/lib/safe.ts
src/lib/thread.ts
test/create-wallet.test.mjs

index 93810e493f5f90cc03775d700b716882b093d707..276d2cf929df61d59a300284825315cf5f8a1c31 100644 (file)
                "package.json.license"
        ],
        "main": "dist/main.js",
-       "browser": "dist/main.min.js",
+       "browser": {
+               "dist/main.min.js": true,
+               "node:worker_threads": false
+       },
        "repository": {
                "type": "git",
                "url": "git+https://zoso.dev/libnemo.git"
        },
        "scripts": {
-               "build": "rm -rf dist && tsc && esbuild main.min=dist/main.js global.min=dist/global.js --outdir=dist --target=es2022 --format=esm --platform=browser --bundle --minify --sourcemap",
-               "test": "npm run build && node --test --env-file .env",
+               "build": "rm -rf dist && tsc && esbuild main.min=dist/main.js global.min=dist/global.js --outdir=dist --target=es2022 --format=esm --bundle --minify --sourcemap",
+               "test": "npm run build -- --platform=node && node --test --env-file .env",
                "test:coverage": "npm run test -- --experimental-test-coverage",
                "test:coverage:report": "npm run test:coverage -- --test-reporter=lcov --test-reporter-destination=coverage.info && genhtml coverage.info --output-directory test/coverage && rm coverage.info && xdg-open test/coverage/index.html"
        },
index e413fff5109f627170e1bb3f31c1a4f4f583e005..c660e4a4bd95ab0a2dbdd6634352d7f6bf97bfc7 100644 (file)
@@ -9,12 +9,22 @@
 * calling function as an ArrayBuffer. This buffer can ultimately be processed
 * using the `importKey()` method of the SubtleCrypto interface.
 */
-function passkey () {
+async function passkey () {
+       /**
+       * Polyfill for window methods which do not exist when executing Node.js tests.
+       */
+       if (globalThis.addEventListener == null || globalThis.postMessage == null) {
+               const { isMainThread, parentPort } = await import('node:worker_threads')
+               if (!isMainThread && parentPort) {
+                       var addEventListener = Object.getPrototypeOf(parentPort).addListener.bind(parentPort)
+                       var postMessage = Object.getPrototypeOf(parentPort).postMessage.bind(parentPort)
+               }
+       }
+
        /**
        * Message listener for this Web Worker thread.
        */
        addEventListener('message', async (message: any) => {
-               await polyfill()
                const { password, iv } = message.data ?? message
                const keyBuffer = await keygen(password, iv)
                postMessage(keyBuffer, { transfer: [keyBuffer] })
@@ -46,21 +56,7 @@ function passkey () {
                )
                return crypto.subtle.exportKey('raw', key)
        }
-
-       /**
-       * Polyfill for window methods which do not exist when executing Node.js tests.
-       */
-       async function polyfill () {
-               if (addEventListener == null || postMessage == null) {
-                       const { isMainThread, parentPort } = await import('node:worker_threads')
-                       if (!isMainThread && parentPort) {
-                               var addEventListener = Object.getPrototypeOf(parentPort).addListener.bind(parentPort)
-                               var postMessage = Object.getPrototypeOf(parentPort).postMessage.bind(parentPort)
-                       }
-               }
-       }
 }
 
-const worker = new Blob(['(', passkey.toString(), ')()'], { type: 'application/javascript' })
-const workerUrl = URL.createObjectURL(worker)
+const workerUrl = `data:text/javascript,(${passkey.toString()})()`
 export { workerUrl }
index 91df9fa0984d02be2918404ce31360941f2f667f..9c541ed9d7f464cc34bf04780604039cbe47f963 100644 (file)
@@ -15,7 +15,6 @@ export class Safe {
        constructor () {
                this.#storage = globalThis.sessionStorage
                this.#thread = new Thread(workerUrl)
-               URL.revokeObjectURL(workerUrl)
        }
 
        /**
@@ -49,9 +48,10 @@ export class Safe {
                const iv = new Entropy()
                if (typeof passkey === 'string') {
                        try {
-                               // this.#thread.work({ password: passkey, iv: iv.bytes })
-                               passkey = await subtle.importKey('raw', utf8.toBytes(passkey), 'PBKDF2', false, ['deriveBits', 'deriveKey'])
-                               passkey = await subtle.deriveKey({ name: 'PBKDF2', hash: 'SHA-512', salt: iv.bytes, iterations: 210000 }, passkey, { name: 'AES-GCM', length: 256 }, false, ['encrypt'])
+                               const keyBuffer = await this.#thread.work({ password: passkey, iv: iv.bytes })
+                               passkey = await subtle.importKey('raw', keyBuffer, 'AES-GCM', false, ['encrypt'])
+                               // passkey = await subtle.importKey('raw', utf8.toBytes(passkey), 'PBKDF2', false, ['deriveBits', 'deriveKey'])
+                               // passkey = await subtle.deriveKey({ name: 'PBKDF2', hash: 'SHA-512', salt: iv.bytes, iterations: 210000 }, passkey, { name: 'AES-GCM', length: 256 }, false, ['encrypt'])
                        } catch (err) {
                                throw new Error(ERR_MSG)
                        }
index 79addc42612a218e26e98049dca9ba434bccd807..948d6036b56f3f15b9a189190424a18274e8a22b 100644 (file)
@@ -31,8 +31,8 @@ export class Thread {
 
   constructor (url: string | URL) {
     this.#worker = new Worker(new URL(url, import.meta.url), { type: 'module' })
-    this.#worker.addEventListener('message', (event) => {
-      const result = event.data ?? event
+    this.#worker.addEventListener('message', (message) => {
+      const result = message.data ?? message
       if (this.#task == null) {
         throw new ReferenceError(`Error resolving Worker result: ${result}`)
       }
index 4e63dc3efa91e3508777946c97aba18ef2c93958..b1ba84b32e10325ed450ae75f2174006f325ae22 100644 (file)
@@ -18,8 +18,11 @@ describe('creating a new wallet', async () => {
                await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
 \r
                assert.ok('id' in wallet)\r
+               assert.ok(/[A-Fa-f0-9]{32,64}/.test(wallet.id))\r
                assert.ok('mnemonic' in wallet)\r
+               assert.ok(/^(?:[a-z]{3,} ){11,23}[a-z]{3,}$/.test(wallet.mnemonic))\r
                assert.ok('seed' in wallet)\r
+               assert.ok(/[A-Fa-f0-9]{32,64}/.test(wallet.seed))\r
        })\r
 \r
        it('BLAKE2b wallet with random entropy', async () => {\r
@@ -27,8 +30,11 @@ describe('creating a new wallet', async () => {
                await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
 \r
                assert.ok('id' in wallet)\r
+               assert.ok(/[A-Fa-f0-9]{32,64}/.test(wallet.id))\r
                assert.ok('mnemonic' in wallet)\r
+               assert.ok(/^(?:[a-z]{3,} ){11,23}[a-z]{3,}$/.test(wallet.mnemonic))\r
                assert.ok('seed' in wallet)\r
+               assert.ok(/[A-Fa-f0-9]{32,64}/.test(wallet.seed))\r
        })\r
 \r
        it('BIP-44 replace invalid salt with empty string', async () => {\r
@@ -54,3 +60,21 @@ describe('creating a new wallet', async () => {
                assert.ok(wallet)\r
        })\r
 })\r
+\r
+describe('wallet generation performance', { skip: true }, async () => {\r
+       it('performance test creating BIP-44 wallets', async () => {\r
+               const wallets = []\r
+               for (let i = 0x100; i > 0; i--) {\r
+                       wallets.push(await Bip44Wallet.create(NANO_TEST_VECTORS.PASSWORD))\r
+               }\r
+               assert.equal(wallets.length, 0x100)\r
+       })\r
+\r
+       it('performance test creating BLAKE2b wallets', async () => {\r
+               const wallets = []\r
+               for (let i = 0x100; i > 0; i--) {\r
+                       wallets.push(await Blake2bWallet.create(NANO_TEST_VECTORS.PASSWORD))\r
+               }\r
+               assert.equal(wallets.length, 0x100)\r
+       })\r
+})\r