jedisct1 / libsodium.js

libsodium compiled to Webassembly and pure JavaScript, with convenient wrappers.
Other
968 stars 138 forks source link

sodium.ready never resolves in webworker if this webworker is created as a blob #320

Open rwasef1830 opened 1 year ago

rwasef1830 commented 1 year ago

Hello,

Using the technique described here: https://stackoverflow.com/a/73621351/111830

If I importScripts("sodium.js") in such a blob created worker, sodium.ready inside the worker never fires. If the web worker is created "normally" with an external js file, it works correctly.

Complete test case of issue:

<!DOCTYPE html>
<html>
  <body>
    <script>
        class Threadable extends Function {       
            constructor(f) {
                super("...as",`return (${f.toString()}).apply(this, as)`);
            }

            spawn(...as) {
                var code = `self.onmessage = m => self.postMessage((${this.toString()}).apply(self,m.data));`,
                    blob = new Blob([code], {type: "text/javascript"}),
                    wrkr = new Worker(window.URL.createObjectURL(blob));

                return new Promise((v, x) => (wrkr.onmessage = m => (v(m.data), wrkr.terminate()), 
                                              wrkr.onerror   = e => (x(e.message), wrkr.terminate()), 
                                              wrkr.postMessage(as)));
            }
        };

        const generateSecurityHashChunkWorker = new Threadable((salt, secret) => {
            const sodiumOnload = async sodium => {
                                // Never enters here
                console.log("Sodium ready fired");

                let saltSodium = sodium.from_string(salt);
                saltSodium = await sodium.crypto_generichash(16, saltSodium, null);
                const secretSodium = sodium.from_string(iterationSecret);

                const hash = await sodium.crypto_pwhash(
                    64,
                    secretSodium,
                    saltSodium,
                    sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, 
                    sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
                    sodium.crypto_pwhash_ALG_ARGON2ID13);

                console.log("sodium result", hash);
                postMessage(hash);
            };
            importScripts('https://fastly.jsdelivr.net/gh/jedisct1/libsodium.js@master/dist/browsers-sumo/sodium.js');
            sodium.ready.then(async () => await sodiumOnload(sodium));
            console.log("sodium.ready", sodium.ready);
        });

        const generateSecurityHashAsync = async (salt, secret) => {
            if (!salt) {
                throw "salt cannot be empty";
            }

            if (!secret) {
                throw "secret cannot be empty";
            }

            const start = new Date().getTime();
            const worker = generateSecurityHashChunkWorker.spawn(
                salt, 
                secret);
            const hash = await worker;
            console.log("Hash result from awaiting worker", hash);

            const timeMilliseconds = new Date().getTime() - start;
            console.log("Security hash time (ms)", timeMilliseconds, "size", hash?.length);

            return hash;
        };

        document.addEventListener("DOMContentLoaded", async event => {
            const salt = "Test";
            const password = "Password";
            const hash = await generateSecurityHashAsync(salt, password);
            console.log(hash);
        });
    </script>
  </body>
</html>