Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a way to make it non-blocking? #96

Open
VityaSchel opened this issue Feb 16, 2024 · 2 comments
Open

Is there a way to make it non-blocking? #96

VityaSchel opened this issue Feb 16, 2024 · 2 comments

Comments

@VityaSchel
Copy link

When using crypto_pwhash_MEMLIMIT_MODERATE/1024 memory (256 MiB), 3 iterations, it takes more than a second to hash and while it hashing, render loop is blocked and page is frozen
I'm using service worker anyway, maybe use that here?

@VityaSchel
Copy link
Author

I resolved this using custom service worker with WASM.

First, you need webpack or other bundler to bundle everything into single js file in service worker. Then install argon2-browser and use this code:

import argon2 from 'argon2-browser'

const crypto_pwhash_SALTBYTES = 16
const crypto_pwhash_MEMLIMIT_MODERATE = 268435456
const crypto_pwhash_OPSLIMIT_MODERATE = 3
const crypto_aead_xchacha20poly1305_ietf_KEYBYTES = 32

const channel = new BroadcastChannel('sw-messages')

self.addEventListener('message', async event => {
  if(typeof event.data === 'object') {
    if (event.data.type === 'hash' && 'plain' in event.data) {
      const OLD_ENC_SALT = new Uint8Array(crypto_pwhash_SALTBYTES)
      const result = await argon2.hash({
        pass: event.data.plain,
        salt: OLD_ENC_SALT,
        time: crypto_pwhash_OPSLIMIT_MODERATE,
        mem: crypto_pwhash_MEMLIMIT_MODERATE / 1024,
        hashLen: crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
        parallelism: 1,
        type: argon2.ArgonType.Argon2id
      })
      channel.postMessage({ type: 'hash_result', result, plain: event.data.plain })
    }
  }
})

Then build it into single file, import with service worker and you can use it like this on your website:

const hashChannel = new BroadcastChannel('sw-messages')

const plain = 'what you want to hash goes here'
const sw = navigator.serviceWorker.controller
      if (!sw) return console.error('Service Worker not ready')
      sw.postMessage({ type: 'hash', plain })
      const subscription = (event: MessageEvent<{ type: 'hash_result', result: import('argon2-browser').Argon2BrowserHashResult, plain: string }>) => {
        if (
          typeof event.data === 'object' 
          && event.data.type === 'hash_result' 
          && 'result' in event.data
          && event.data.plain === plain
        ) {
          hashChannel.removeEventListener('message', subscription)
          resolve(event.data.result.hash)
        }
      }
      hashChannel.addEventListener('message', subscription)

@VityaSchel
Copy link
Author

Oh and this is the config I used for webpack to correctly bundle wasm:

/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path')
const { IgnorePlugin } = require('webpack')

/** @type {import('webpack').Configuration} */
const config = {
  mode: 'production',
  entry: __dirname + '/index.ts',
  output: {
    path: path.resolve(__dirname, '../public'),
    filename: 'custom-worker.js'
  },
  module: {
    rules: [
      {
        test: /\.wasm$/,
        type: 'javascript/auto',
        use: [{
          loader: 'base64-loader'
        }]
      }
    ],
    noParse: /\.wasm$/
  },
  plugins: [
    new IgnorePlugin({ resourceRegExp: /\/__tests__\// })
  ],
  resolve: {
    fallback: {
      path: require.resolve('path-browserify'),
      fs: false
    }
  }
}
module.exports = config

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant