├── .github ├── FUNDING.yml └── workflows │ └── build.yaml ├── .gitignore ├── .gitmodules ├── .npmignore ├── .prettierrc ├── CMakeLists.txt ├── LICENSE ├── README.md ├── build-wasm.sh ├── build.sh ├── ci └── install.sh ├── clean-cmake.sh ├── docs ├── dist │ ├── argon2-simd.wasm │ ├── argon2.js │ └── argon2.wasm ├── index.html └── js │ ├── calc.js │ ├── main.js │ ├── text-encoder-lite.min.js │ └── worker.js ├── examples ├── node │ ├── argon2-demo-node.js │ └── package.json ├── react │ ├── config-overrides.js │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── index.css │ │ └── index.js ├── vanilla │ ├── index.html │ ├── package.json │ └── run.js └── webpack │ ├── argon2-demo-webpack.js │ ├── index.html │ ├── package.json │ └── webpack.config.js ├── lib └── argon2.js ├── package-lock.json ├── package.json ├── perf-test.c ├── src └── argon2_library.c ├── test ├── browser.js ├── node.js ├── suite │ ├── hash.js │ └── verify.js ├── vanilla │ └── index.html └── webpack │ ├── index.html │ ├── package.json │ ├── tests.js │ └── webpack.config.js └── webpack.config.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: antelle 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - '**' 6 | tags: 7 | - '*' 8 | pull_request: 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | submodules: true 17 | - name: Install prerequisites 18 | run: ci/install.sh 19 | - name: Upgrade Node.js 20 | uses: actions/setup-node@v2 21 | with: 22 | node-version: '16' 23 | registry-url: 'https://registry.npmjs.org' 24 | - name: NPM install 25 | run: npm ci 26 | - name: Build 27 | run: source emsdk/emsdk_env.sh && ./build.sh 28 | - name: Test 29 | run: npm test 30 | - name: Bundle 31 | run: npm run build-bundle 32 | - name: Package 33 | run: npm pack 34 | - name: Publish to npm 35 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 36 | run: npm publish 37 | env: 38 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeFiles 2 | CMakeCache.txt 3 | cmake_install.cmake 4 | libargon2.a 5 | libargon2.so 6 | libargon2.dylib 7 | .DS_Store 8 | src/*.o 9 | src/blake2/*.o 10 | genkat 11 | .idea 12 | *.pyc 13 | testcase 14 | *.gcda 15 | *.gcno 16 | *.gcov 17 | bench 18 | vs2015/build 19 | Argon2.sdf 20 | Argon2.VC.opendb 21 | *.zip 22 | *.tar.gz 23 | Makefile 24 | examples/**/package-lock.json 25 | node_modules/ 26 | dist/ 27 | examples/**/dist/ 28 | test/**/package-lock.json 29 | test/webpack/node_modules/ 30 | test/webpack/dist/ 31 | *.tgz 32 | package/ 33 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "argon2"] 2 | path = argon2 3 | url = git@github.com:P-H-C/phc-winner-argon2.git 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !dist/argon2.js 3 | !dist/argon2-bundled.min.js 4 | !dist/argon2.wasm 5 | !dist/argon2-simd.wasm 6 | !lib/*.js 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true 4 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | project(agron2) 4 | 5 | add_definitions(-DARGON2_NO_THREADS) 6 | 7 | set(ARCHIVE_OUTPUT_DIRECTORY dist) 8 | set(LIBRARY_OUTPUT_DIRECTORY dist) 9 | set(RUNTIME_OUTPUT_DIRECTORY dist) 10 | set(EXECUTABLE_OUTPUT_PATH dist) 11 | 12 | include_directories(argon2/include) 13 | 14 | set(sources 15 | argon2/src/blake2/blake2b.c 16 | 17 | argon2/src/argon2.c 18 | argon2/src/core.c 19 | argon2/src/encoding.c 20 | argon2/src/thread.c 21 | 22 | src/argon2_library.c 23 | ) 24 | 25 | if(${CMAKE_C_FLAGS} MATCHES simd) 26 | list(APPEND sources argon2/src/opt.c) 27 | else() 28 | list(APPEND sources argon2/src/ref.c) 29 | endif() 30 | 31 | add_executable(argon2 ${sources}) 32 | 33 | set_target_properties(argon2 PROPERTIES OUTPUT_NAME ${OUTPUT_NAME}) 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Antelle 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Argon2 in browser ![Build](https://github.com/antelle/argon2-browser/workflows/Build/badge.svg) 2 | 3 | Argon2 is a password-hashing function, the winner of Password Hashing Competition. Here Argon2 library is compiled for browser runtime. 4 | 5 | [Live demo](https://antelle.github.io/argon2-browser) 6 | 7 | [More about Argon2](https://github.com/P-H-C/phc-winner-argon2) 8 | 9 | [Usage](#usage) 10 | 11 | ## The numbers 12 | 13 | | | Time, ms (lower is better) | 14 | |-------------------|----------------------------| 15 | | Chrome WASM | 225 | 16 | | Chrome WASM+SIMD | 119 | 17 | | Firefox WASM | 195 | 18 | | Firefox WASM+SIMD | 135 | 19 | | Safari WASM | 174 | 20 | | Native -O3 SSE | 15 | 21 | | Native -O3 | 42 | 22 | | Native -O1 | 55 | 23 | | Native -O0 | 395 | 24 | 25 | ## Test Environment 26 | 27 | Environment used to get the numbers above: 28 | 29 | Algorithm parameters (`-d -t 100 -m 10 -p 1`): 30 | - iterations: 100 31 | - memory: 1MiB (1024 KiB) 32 | - hash length: 32 33 | - parallelism: 1 34 | - argon2d 35 | 36 | Environment: 37 | 38 | - MacBook pro 2020, Intel Core i7, 2.3GHz (x64), macOS 10.14.6 (18G95) 39 | - Chrome 85.0.4183.83 (Official Build) 40 | - Firefox 80.0.1 41 | - Safari 13.1.2 (15609.3.5.1.3) 42 | - native argon2 compiled from https://github.com/P-H-C/phc-winner-argon2 @440ceb9 43 | 44 | ## Code size 45 | 46 | `ll -h dist` 47 | 48 | | File | Code size, kB | 49 | |-------------|---------------| 50 | | argon2.js | 14 | 51 | | argon2.wasm | 25 | 52 | 53 | ## Is Argon2 modified? 54 | 55 | No, it's used a submodule from upstream. 56 | 57 | ## SIMD 58 | 59 | SIMD is not quite here in WebAssembly, however for those who would like to give it a try, 60 | we already provide a working build with SIMD. At the moment it works only in Chrome, 61 | to be able to use it, you need to either add 62 | [this origin trial](https://developers.chrome.com/origintrials/#/view_trial/-4708513410415853567) to your website, 63 | or enable the SIMD feature in Chrome flags. 64 | 65 | More about WebAssembly SIMD support in V8: https://v8.dev/features/simd 66 | 67 | On Firefox you need to enable `javascript.options.wasm_simd` option in about:config. 68 | 69 | To use the SIMD version, load `argon2-simd.wasm` instead of `argon2.wasm`. 70 | 71 | ## JS Library 72 | 73 | The library can be installed from npm: 74 | ```bash 75 | npm install argon2-browser 76 | ``` 77 | 78 | Then add this script to your HTML or use your favorite bundler: 79 | ```html 80 | 81 | ``` 82 | 83 | Alternatively, you can use the bundled version, this way you can include just one script: 84 | ```html 85 | 86 | ``` 87 | 88 | Calculate the hash: 89 | ```javascript 90 | argon2.hash({ pass: 'password', salt: 'somesalt' }) 91 | .then(h => console.log(h.hash, h.hashHex, h.encoded)) 92 | .catch(e => console.error(e.message, e.code)) 93 | ``` 94 | 95 | Verify the encoded hash (if you need it): 96 | ```javascript 97 | argon2.verify({ pass: 'password', encoded: 'enc-hash' }) 98 | .then(() => console.log('OK')) 99 | .catch(e => console.error(e.message, e.code)) 100 | ``` 101 | 102 | Other parameters: 103 | ```javascript 104 | argon2.hash({ 105 | // required 106 | pass: 'password', 107 | salt: 'salt', 108 | // optional 109 | time: 1, // the number of iterations 110 | mem: 1024, // used memory, in KiB 111 | hashLen: 24, // desired hash length 112 | parallelism: 1, // desired parallelism (it won't be computed in parallel, however) 113 | secret: new Uint8Array([...]), // optional secret data 114 | ad: new Uint8Array([...]), // optional associated data 115 | type: argon2.ArgonType.Argon2d, // Argon2d, Argon2i, Argon2id 116 | }) 117 | // result 118 | .then(res => { 119 | res.hash // hash as Uint8Array 120 | res.hashHex // hash as hex-string 121 | res.encoded // encoded hash, as required by argon2 122 | }) 123 | // or error 124 | .catch(err => { 125 | err.message // error message as string, if available 126 | err.code // numeric error code 127 | }) 128 | ``` 129 | 130 | ```javascript 131 | argon2.verify({ 132 | // required 133 | pass: 'password', 134 | encoded: 'enc-hash', 135 | // optional 136 | secret: new Uint8Array([...]), // optional secret data 137 | ad: new Uint8Array([...]), // optional associated data 138 | type: argon2.ArgonType.Argon2d, // Argon2d, Argon2i, Argon2id. default: guess 139 | }) 140 | // result 141 | .then(res => { 142 | res.hash // hash as Uint8Array 143 | res.hashHex // hash as hex-string 144 | res.encoded // encoded hash, as required by argon2 145 | }) 146 | // or error 147 | .catch(err => { 148 | err.message // error message as string, if available 149 | err.code // numeric error code 150 | }) 151 | ``` 152 | 153 | ## Usage 154 | 155 | You can use this module in several ways: 156 | 157 | 1. write the WASM loader manually, for example, if you need more control over memory ([example](docs/js/calc.js)); 158 | 2. bundle it with WebPack or another bundler ([example](examples/webpack)); 159 | 3. in vanilla js: [example](examples/vanilla); 160 | 4. in node.js: [example](examples/node) (see a note below). 161 | 162 | ## Bundlers 163 | 164 | - WebPack: [examples/webpack](https://github.com/antelle/argon2-browser/tree/master/examples/webpack) 165 | - create-react-app: [examples/react](https://github.com/antelle/argon2-browser/tree/master/examples/react). Derived from [#38](https://github.com/antelle/argon2-browser/issues/38#issuecomment-749690581) 166 | 167 | ## Node.js support 168 | 169 | Of course you [can use](examples/node) generated WASM in node.js, but it's not sensible: you will get much better speed by compiling it as a native node.js addon, which is not that hard. Wait, it's already done, just install [this package](https://github.com/ranisalt/node-argon2). 170 | 171 | ## Is it used anywhere? 172 | 173 | It is! [KeeWeb](https://github.com/keeweb/keeweb) (web-based password manager) is using it as a password hashing function implementation. 174 | [Check out the source code](https://github.com/keeweb/keeweb/blob/develop/app/scripts/util/kdbxweb/kdbxweb-init.js#L11), if you're interested. 175 | 176 | ## Building 177 | 178 | You can build everything with 179 | ```bash 180 | ./build.sh 181 | ``` 182 | 183 | Prerequisites: 184 | - emscripten with WebAssembly support ([howto](http://webassembly.org/getting-started/developers-guide/)) 185 | - CMake 186 | 187 | ## License 188 | 189 | [MIT](https://opensource.org/licenses/MIT) 190 | -------------------------------------------------------------------------------- /build-wasm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | ARGON_JS_EXTRA_C_FLAGS="" 7 | if [[ "$ARGON_JS_BUILD_BUILD_WITH_SIMD" == "1" ]]; then 8 | ARGON_JS_EXTRA_C_FLAGS="-msimd128 -msse2" 9 | fi 10 | 11 | cmake \ 12 | -DOUTPUT_NAME="argon2" \ 13 | -DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake \ 14 | -DCMAKE_VERBOSE_MAKEFILE=OFF \ 15 | -DCMAKE_BUILD_TYPE=MinSizeRel \ 16 | -DCMAKE_C_FLAGS="-O3 $ARGON_JS_EXTRA_C_FLAGS" \ 17 | -DCMAKE_EXE_LINKER_FLAGS="-O3 --memory-init-file 0 \ 18 | -s NO_FILESYSTEM=1 \ 19 | -s 'EXPORTED_FUNCTIONS=[\"_argon2_hash\",\"_argon2_hash_ext\",\"_argon2_verify\",\"_argon2_verify_ext\",\"_argon2_error_message\",\"_argon2_encodedlen\",\"_malloc\",\"_free\"]' \ 20 | -s 'EXPORTED_RUNTIME_METHODS=[\"UTF8ToString\",\"allocate\",\"ALLOC_NORMAL\"]' \ 21 | -s DEMANGLE_SUPPORT=0 \ 22 | -s ASSERTIONS=0 \ 23 | -s NO_EXIT_RUNTIME=1 \ 24 | -s TOTAL_MEMORY=16MB \ 25 | -s BINARYEN_MEM_MAX=2147418112 \ 26 | -s ALLOW_MEMORY_GROWTH=1 \ 27 | -s WASM=1" \ 28 | . 29 | cmake --build . 30 | 31 | shasum dist/argon2.js 32 | shasum dist/argon2.wasm 33 | 34 | perl -pi -e 's/"argon2.js.mem"/null/g' dist/argon2.js 35 | perl -pi -e 's/$/if(typeof module!=="undefined")module.exports=Module;Module.unloadRuntime=function(){if(typeof self!=="undefined"){delete self.Module}Module=jsModule=wasmMemory=wasmTable=asm=buffer=HEAP8=HEAPU8=HEAP16=HEAPU16=HEAP32=HEAPU32=HEAPF32=HEAPF64=undefined;if(typeof module!=="undefined"){delete module.exports}};/' dist/argon2.js 36 | perl -pi -e 's/typeof Module!=="undefined"\?Module:\{};/typeof self!=="undefined"&&typeof self.Module!=="undefined"?self.Module:{};var jsModule=Module;/g' dist/argon2.js 37 | perl -pi -e 's/receiveInstantiatedSource\(output\)\{/receiveInstantiatedSource(output){Module=jsModule;if(typeof self!=="undefined")self.Module=Module;/g' dist/argon2.js 38 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | rm -rf dist 7 | mkdir dist 8 | rm -rf docs/dist 9 | mkdir docs/dist 10 | 11 | ./clean-cmake.sh 12 | ARGON_JS_BUILD_BUILD_WITH_SIMD=1 ./build-wasm.sh 13 | cp dist/argon2.wasm docs/dist/argon2-simd.wasm 14 | mv dist/argon2.wasm dist/argon2-simd.wasm 15 | 16 | ./clean-cmake.sh 17 | ARGON_JS_BUILD_BUILD_WITH_SIMD=0 ./build-wasm.sh 18 | cp dist/argon2.js docs/dist/argon2.js 19 | cp dist/argon2.wasm docs/dist/argon2.wasm 20 | 21 | ./clean-cmake.sh 22 | 23 | echo Done 24 | -------------------------------------------------------------------------------- /ci/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | git clone https://github.com/emscripten-core/emsdk.git 6 | cd emsdk 7 | ./emsdk install latest 8 | ./emsdk activate latest 9 | cd .. 10 | -------------------------------------------------------------------------------- /clean-cmake.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | rm -rf CMakeFiles && 7 | rm -f CMakeCache.txt && 8 | rm -f Makefile && 9 | rm -f cmake_install.cmake 10 | -------------------------------------------------------------------------------- /docs/dist/argon2-simd.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antelle/argon2-browser/d73916b8efad2ef47140a52acd48b166a4ba97bf/docs/dist/argon2-simd.wasm -------------------------------------------------------------------------------- /docs/dist/argon2.js: -------------------------------------------------------------------------------- 1 | var Module=typeof self!=="undefined"&&typeof self.Module!=="undefined"?self.Module:{};var jsModule=Module;var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var ALLOC_NORMAL=0;var ALLOC_STACK=1;function allocate(slab,allocator){var ret;if(allocator==ALLOC_STACK){ret=stackAlloc(slab.length)}else{ret=_malloc(slab.length)}if(slab.subarray||slab.slice){HEAPU8.set(slab,ret)}else{HEAPU8.set(new Uint8Array(slab),ret)}return ret}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;__ATINIT__.push({func:function(){___wasm_call_ctors()}});function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="argon2.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["c"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["k"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){Module=jsModule;if(typeof self!=="undefined")self.Module=Module;receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147418112;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var asmLibraryArg={"a":_emscripten_memcpy_big,"b":_emscripten_resize_heap};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["d"]).apply(null,arguments)};var _argon2_hash=Module["_argon2_hash"]=function(){return(_argon2_hash=Module["_argon2_hash"]=Module["asm"]["e"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["f"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["g"]).apply(null,arguments)};var _argon2_verify=Module["_argon2_verify"]=function(){return(_argon2_verify=Module["_argon2_verify"]=Module["asm"]["h"]).apply(null,arguments)};var _argon2_error_message=Module["_argon2_error_message"]=function(){return(_argon2_error_message=Module["_argon2_error_message"]=Module["asm"]["i"]).apply(null,arguments)};var _argon2_encodedlen=Module["_argon2_encodedlen"]=function(){return(_argon2_encodedlen=Module["_argon2_encodedlen"]=Module["asm"]["j"]).apply(null,arguments)};var _argon2_hash_ext=Module["_argon2_hash_ext"]=function(){return(_argon2_hash_ext=Module["_argon2_hash_ext"]=Module["asm"]["l"]).apply(null,arguments)};var _argon2_verify_ext=Module["_argon2_verify_ext"]=function(){return(_argon2_verify_ext=Module["_argon2_verify_ext"]=Module["asm"]["m"]).apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return(stackAlloc=Module["stackAlloc"]=Module["asm"]["n"]).apply(null,arguments)};Module["allocate"]=allocate;Module["UTF8ToString"]=UTF8ToString;Module["ALLOC_NORMAL"]=ALLOC_NORMAL;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run();if(typeof module!=="undefined")module.exports=Module;Module.unloadRuntime=function(){if(typeof self!=="undefined"){delete self.Module}Module=jsModule=wasmMemory=wasmTable=asm=buffer=HEAP8=HEAPU8=HEAP16=HEAPU16=HEAP32=HEAPU32=HEAPF32=HEAPF64=undefined;if(typeof module!=="undefined"){delete module.exports}}; 2 | -------------------------------------------------------------------------------- /docs/dist/argon2.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antelle/argon2-browser/d73916b8efad2ef47140a52acd48b166a4ba97bf/docs/dist/argon2.wasm -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Argon2 in browser 6 | 7 | 8 | 9 |
10 |

Argon2 in browser

11 |

Argon2 is a password-hashing function that summarizes the state of the art in the design of memory-hard functions and can be used to hash passwords for credential storage, key derivation, or other applications.

12 |

Here Argon2 library is compiled for browser runtime. Statistics, js library, source and docs on GitHub.

13 |
14 |
15 | 16 |
17 |
18 | 19 |
20 | 21 |
22 |
23 | 24 |
25 | 26 |
27 |
28 | 29 |
KiB
30 |
31 |
32 |
33 | 34 |
35 | 36 |
37 |
38 | 39 |
40 | 41 |
42 |
43 | 44 |
45 | 46 |
47 |
48 | 49 |
50 | 51 |
52 | 55 | 58 | 61 |
62 |
63 | 64 |
65 |
66 | 67 | 68 | 69 | GitHub Repo 70 |
71 |
72 | 73 |
74 | 75 |
76 | 77 |
78 |
79 |
80 |
81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /docs/js/calc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var global = typeof window === 'undefined' ? self : window; 4 | var root = typeof window === 'undefined' ? '../' : ''; 5 | 6 | function calc(fn) { 7 | return function (e) { 8 | e.preventDefault(); 9 | try { 10 | fn(); 11 | } catch (e) { 12 | log('Error: ' + e); 13 | } 14 | }; 15 | } 16 | 17 | function calcWasm() { 18 | calcBinaryen('native-wasm'); 19 | } 20 | 21 | function calcSimd() { 22 | calcBinaryen('native-wasm', { simd: true }); 23 | } 24 | 25 | function calcBinaryen(method, options) { 26 | clearLog(); 27 | 28 | if (!global.WebAssembly) { 29 | log( 30 | "Your browser doesn't support WebAssembly, please try it in Chrome Canary or Firefox Nightly with WASM flag enabled" 31 | ); 32 | return; 33 | } 34 | 35 | const mem = getArg().mem; 36 | 37 | log('Testing Argon2 using Binaryen ' + method); 38 | if ( 39 | global.Module && 40 | global.Module.wasmJSMethod === method && 41 | global.Module._argon2_hash_ext 42 | ) { 43 | log('Calculating hash...'); 44 | setTimeout(calcHash, 10); 45 | return; 46 | } 47 | 48 | const KB = 1024 * 1024; 49 | const MB = 1024 * KB; 50 | const GB = 1024 * MB; 51 | const WASM_PAGE_SIZE = 64 * 1024; 52 | 53 | const totalMemory = (2 * GB - 64 * KB) / 1024 / WASM_PAGE_SIZE; 54 | const initialMemory = Math.min( 55 | Math.max(Math.ceil((mem * 1024) / WASM_PAGE_SIZE), 256) + 256, 56 | totalMemory 57 | ); 58 | log( 59 | 'Memory: ' + 60 | initialMemory + 61 | ' pages (' + 62 | Math.round(initialMemory * 64) + 63 | ' KB)', 64 | totalMemory 65 | ); 66 | const wasmMemory = new WebAssembly.Memory({ 67 | initial: initialMemory, 68 | maximum: totalMemory, 69 | }); 70 | 71 | global.Module = { 72 | print: log, 73 | printErr: log, 74 | setStatus: log, 75 | wasmBinary: null, 76 | wasmJSMethod: method, 77 | wasmMemory: wasmMemory, 78 | buffer: wasmMemory.buffer, 79 | TOTAL_MEMORY: initialMemory * WASM_PAGE_SIZE, 80 | }; 81 | 82 | var wasmFileName = 'argon2.wasm'; 83 | if (options && options.simd) { 84 | wasmFileName = 'argon2-simd.wasm'; 85 | } 86 | 87 | log('Loading wasm...'); 88 | var xhr = new XMLHttpRequest(); 89 | xhr.open('GET', root + 'dist/' + wasmFileName, true); 90 | xhr.responseType = 'arraybuffer'; 91 | xhr.onload = function () { 92 | global.Module.wasmBinary = xhr.response; 93 | global.Module.postRun = calcHash; 94 | var ts = now(); 95 | log('Wasm loaded, loading script...'); 96 | loadScript( 97 | root + 'dist/argon2.js', 98 | function () { 99 | log('Script loaded in ' + Math.round(now() - ts) + 'ms'); 100 | log('Calculating hash...'); 101 | }, 102 | function () { 103 | log('Error loading script'); 104 | } 105 | ); 106 | }; 107 | xhr.onerror = function () { 108 | log('Error loading wasm'); 109 | }; 110 | xhr.send(null); 111 | } 112 | 113 | function calcHash() { 114 | var arg = getArg(); 115 | if (!Module._argon2_hash_ext) { 116 | return log('Error'); 117 | } 118 | log( 119 | 'Params: ' + 120 | Object.keys(arg) 121 | .map(function (key) { 122 | return key + '=' + arg[key]; 123 | }) 124 | .join(', ') 125 | ); 126 | var dt = now(); 127 | var t_cost = (arg && arg.time) || 10; 128 | var m_cost = (arg && arg.mem) || 1024; 129 | var parallelism = (arg && arg.parallelism) || 1; 130 | var passEncoded = encodeUtf8(arg.pass || 'password'); 131 | var pwd = allocateArray(passEncoded); 132 | var pwdlen = passEncoded.length; 133 | var saltEncoded = encodeUtf8(arg.salt || 'somesalt'); 134 | var argon2_type = (arg && arg.type) || 0; 135 | var salt = allocateArray(saltEncoded); 136 | var saltlen = saltEncoded.length; 137 | var hash = Module.allocate( 138 | new Array((arg && arg.hashLen) || 32), 139 | 'i8', 140 | Module.ALLOC_NORMAL 141 | ); 142 | var hashlen = (arg && arg.hashLen) || 32; 143 | var encodedlen = Module._argon2_encodedlen( 144 | t_cost, 145 | m_cost, 146 | parallelism, 147 | saltlen, 148 | hashlen, 149 | argon2_type 150 | ); 151 | var encoded = Module.allocate( 152 | new Array(encodedlen + 1), 153 | 'i8', 154 | Module.ALLOC_NORMAL 155 | ); 156 | var secret = 0; 157 | var secretlen = 0; 158 | var ad = 0; 159 | var adlen = 0; 160 | var version = 0x13; 161 | var err; 162 | try { 163 | var res = Module._argon2_hash_ext( 164 | t_cost, 165 | m_cost, 166 | parallelism, 167 | pwd, 168 | pwdlen, 169 | salt, 170 | saltlen, 171 | hash, 172 | hashlen, 173 | encoded, 174 | encodedlen, 175 | argon2_type, 176 | secret, 177 | secretlen, 178 | ad, 179 | adlen, 180 | version 181 | ); 182 | } catch (e) { 183 | err = e; 184 | } 185 | var elapsed = now() - dt; 186 | if (res === 0 && !err) { 187 | var hashArr = []; 188 | for (var i = hash; i < hash + hashlen; i++) { 189 | hashArr.push(Module.HEAP8[i]); 190 | } 191 | log('Encoded: ' + Module.UTF8ToString(encoded)); 192 | log( 193 | 'Hash: ' + 194 | hashArr 195 | .map(function (b) { 196 | return ('0' + (0xff & b).toString(16)).slice(-2); 197 | }) 198 | .join('') 199 | ); 200 | log('Elapsed: ' + Math.round(elapsed) + 'ms'); 201 | } else { 202 | try { 203 | if (!err) { 204 | err = Module.UTF8ToString(Module._argon2_error_message(res)); 205 | } 206 | } catch (e) {} 207 | log('Error: ' + res + (err ? ': ' + err : '')); 208 | } 209 | try { 210 | Module._free(pwd); 211 | Module._free(salt); 212 | Module._free(hash); 213 | Module._free(encoded); 214 | } catch (e) {} 215 | } 216 | 217 | function encodeUtf8(str) { 218 | return new TextEncoder().encode(str); 219 | } 220 | 221 | function allocateArray(arr) { 222 | return Module.allocate(arr, 'i8', Module.ALLOC_NORMAL); 223 | } 224 | 225 | function now() { 226 | return global.performance ? performance.now() : Date.now(); 227 | } 228 | -------------------------------------------------------------------------------- /docs/js/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | document.getElementById('frm').addEventListener('submit', function (e) { 4 | e.preventDefault(); 5 | }); 6 | document 7 | .getElementById('btnCalcWasm') 8 | .addEventListener('click', calc(calcWasm)); 9 | document 10 | .getElementById('btnCalcWasmWorker') 11 | .addEventListener('click', function () { 12 | calcWorker('wasm'); 13 | }); 14 | document 15 | .getElementById('btnCalcWasmSimd') 16 | .addEventListener('click', function () { 17 | calcWorker('simd'); 18 | }); 19 | 20 | function loadScript(src, onload, onerror) { 21 | var el = document.createElement('script'); 22 | el.src = src; 23 | el.onload = onload; 24 | el.onerror = onerror; 25 | document.body.appendChild(el); 26 | } 27 | 28 | var worker; 29 | function calcWorker(method) { 30 | clearLog(); 31 | if (worker) { 32 | if (worker.method === method) { 33 | log('Using loaded worker'); 34 | worker.postMessage({ calc: method, arg: getArg() }); 35 | return; 36 | } else { 37 | worker.terminate(); 38 | } 39 | } 40 | log('Starting worker...'); 41 | worker = new Worker('js/worker.js'); 42 | worker.method = method; 43 | var loaded = false; 44 | worker.onmessage = function (e) { 45 | log(e.data.msg); 46 | if (!loaded) { 47 | loaded = true; 48 | worker.postMessage({ calc: method, arg: getArg() }); 49 | } 50 | }; 51 | } 52 | 53 | function getArg() { 54 | return { 55 | pass: document.getElementById('txtPassword').value || 'password', 56 | salt: document.getElementById('txtSalt').value || 'somesalt', 57 | time: +(document.getElementById('txtIt').value || 1), 58 | mem: +(document.getElementById('txtMem').value || 1024), 59 | hashLen: +(document.getElementById('txtLen').value || 32), 60 | parallelism: +(document.getElementById('txtPar').value || 1), 61 | type: +document.querySelector('[name=radioType]:checked').value, 62 | }; 63 | } 64 | 65 | var logTs = 0; 66 | 67 | function log(msg) { 68 | if (!msg) { 69 | return; 70 | } 71 | var txtRes = document.getElementById('txtRes'); 72 | var elapsedMs = Math.round(performance.now() - logTs); 73 | var elapsedSec = (elapsedMs / 1000).toFixed(3); 74 | var elapsed = leftPad(elapsedSec, 6); 75 | txtRes.value += (txtRes.value ? '\n' : '') + '[' + elapsed + '] ' + msg; 76 | } 77 | 78 | function leftPad(str, len) { 79 | str = str.toString(); 80 | while (str.length < len) { 81 | str = '0' + str; 82 | } 83 | return str; 84 | } 85 | 86 | function clearLog() { 87 | logTs = performance.now(); 88 | document.getElementById('txtRes').value = ''; 89 | } 90 | -------------------------------------------------------------------------------- /docs/js/text-encoder-lite.min.js: -------------------------------------------------------------------------------- 1 | // From https://github.com/solderjs/TextEncoderLite, Apache2 License 2 | // https://raw.githubusercontent.com/solderjs/TextEncoderLite/master/LICENSE.md 3 | 4 | function TextEncoder() {} 5 | function TextDecoder() {} 6 | (function() { 7 | 'use strict'; 8 | function utf8ToBytes(string, units) { 9 | units = units || Infinity; 10 | var codePoint; 11 | var length = string.length; 12 | var leadSurrogate = null; 13 | var bytes = []; 14 | var i = 0; 15 | for (; i < length; i++) { 16 | codePoint = string.charCodeAt(i); 17 | if (codePoint > 55295 && codePoint < 57344) { 18 | if (leadSurrogate) { 19 | if (codePoint < 56320) { 20 | if ((units -= 3) > -1) bytes.push(239, 191, 189); 21 | leadSurrogate = codePoint; 22 | continue; 23 | } else { 24 | codePoint = 25 | ((leadSurrogate - 55296) << 10) | 26 | (codePoint - 56320) | 27 | 65536; 28 | leadSurrogate = null; 29 | } 30 | } else { 31 | if (codePoint > 56319) { 32 | if ((units -= 3) > -1) bytes.push(239, 191, 189); 33 | continue; 34 | } else if (i + 1 === length) { 35 | if ((units -= 3) > -1) bytes.push(239, 191, 189); 36 | continue; 37 | } else { 38 | leadSurrogate = codePoint; 39 | continue; 40 | } 41 | } 42 | } else if (leadSurrogate) { 43 | if ((units -= 3) > -1) bytes.push(239, 191, 189); 44 | leadSurrogate = null; 45 | } 46 | if (codePoint < 128) { 47 | if ((units -= 1) < 0) break; 48 | bytes.push(codePoint); 49 | } else if (codePoint < 2048) { 50 | if ((units -= 2) < 0) break; 51 | bytes.push((codePoint >> 6) | 192, (codePoint & 63) | 128); 52 | } else if (codePoint < 65536) { 53 | if ((units -= 3) < 0) break; 54 | bytes.push( 55 | (codePoint >> 12) | 224, 56 | ((codePoint >> 6) & 63) | 128, 57 | (codePoint & 63) | 128 58 | ); 59 | } else if (codePoint < 2097152) { 60 | if ((units -= 4) < 0) break; 61 | bytes.push( 62 | (codePoint >> 18) | 240, 63 | ((codePoint >> 12) & 63) | 128, 64 | ((codePoint >> 6) & 63) | 128, 65 | (codePoint & 63) | 128 66 | ); 67 | } else { 68 | throw new Error('Invalid code point'); 69 | } 70 | } 71 | return bytes; 72 | } 73 | function utf8Slice(buf, start, end) { 74 | var res = ''; 75 | var tmp = ''; 76 | end = Math.min(buf.length, end || Infinity); 77 | start = start || 0; 78 | for (var i = start; i < end; i++) { 79 | if (buf[i] <= 127) { 80 | res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]); 81 | tmp = ''; 82 | } else { 83 | tmp += '%' + buf[i].toString(16); 84 | } 85 | } 86 | return res + decodeUtf8Char(tmp); 87 | } 88 | function decodeUtf8Char(str) { 89 | try { 90 | return decodeURIComponent(str); 91 | } catch (err) { 92 | return String.fromCharCode(65533); 93 | } 94 | } 95 | TextEncoder.prototype.encode = function(str) { 96 | var result; 97 | if ('undefined' === typeof Uint8Array) { 98 | result = utf8ToBytes(str); 99 | } else { 100 | result = new Uint8Array(utf8ToBytes(str)); 101 | } 102 | return result; 103 | }; 104 | TextDecoder.prototype.decode = function(bytes) { 105 | return utf8Slice(bytes, 0, bytes.length); 106 | }; 107 | })(); 108 | if (typeof module === 'object' && module) { 109 | module.exports.TextDecoder = TextDecoder; 110 | module.exports.TextEncoder = TextEncoder; 111 | } 112 | -------------------------------------------------------------------------------- /docs/js/worker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var calcHashArg; 4 | 5 | self.onmessage = function (e) { 6 | self.postMessage('calc:' + e.data.calc); 7 | calcHashArg = e.data.arg; 8 | switch (e.data.calc) { 9 | case 'wasm': 10 | calcWasm(); 11 | break; 12 | case 'simd': 13 | calcSimd(); 14 | break; 15 | } 16 | }; 17 | 18 | function clearLog() {} 19 | 20 | function log(msg) { 21 | self.postMessage({ msg: msg }); 22 | } 23 | 24 | function loadScript(script, callback, errorCallback) { 25 | try { 26 | importScripts(script); 27 | } catch (e) { 28 | console.error('Error loading script', script, e); 29 | errorCallback(e); 30 | return; 31 | } 32 | callback(); 33 | } 34 | 35 | function getArg() { 36 | return calcHashArg; 37 | } 38 | 39 | if (navigator.userAgent.indexOf('Edge') >= 0) { 40 | importScripts('text-encoder-lite.min.js'); 41 | } 42 | importScripts('calc.js'); 43 | self.postMessage({ msg: 'Worker started' }); 44 | -------------------------------------------------------------------------------- /examples/node/argon2-demo-node.js: -------------------------------------------------------------------------------- 1 | const argon2 = require('argon2-browser'); 2 | 3 | argon2 4 | .hash({ 5 | pass: 'p@ssw0rd', 6 | salt: 'somesalt' 7 | }) 8 | .then(hash => { 9 | console.log(`Encoded: ${hash.encoded}`); 10 | console.log(`Hex: ${hash.hashHex}`); 11 | 12 | argon2 13 | .verify({ 14 | pass: 'p@ssw0rd', 15 | encoded: hash.encoded 16 | }) 17 | .then(() => console.log('Verified OK')) 18 | .catch(e => console.error('Error: ', e)); 19 | }) 20 | .catch(e => console.error('Error: ', e)); 21 | -------------------------------------------------------------------------------- /examples/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "argon2-node", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "argon2-demo-node.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "MIT", 12 | "dependencies": { 13 | "argon2-browser": "^1.13.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/react/config-overrides.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | webpack: function(config, env) { 3 | config.module.rules.push({ 4 | test: /\.wasm$/, 5 | loader: "base64-loader", 6 | type: "javascript/auto", 7 | }); 8 | 9 | config.module.noParse = /\.wasm$/; 10 | 11 | config.module.rules.forEach(rule => { 12 | (rule.oneOf || []).forEach(oneOf => { 13 | if (oneOf.loader && oneOf.loader.indexOf("file-loader") >= 0) { 14 | oneOf.exclude.push(/\.wasm$/); 15 | } 16 | }); 17 | }); 18 | return config 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-argon2", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.10", 7 | "@testing-library/react": "^11.2.6", 8 | "@testing-library/user-event": "^12.8.3", 9 | "argon2-browser": "^1.15.4", 10 | "base64-loader": "^1.0.0", 11 | "react": "^17.0.2", 12 | "react-app-rewired": "^2.1.8", 13 | "react-dom": "^17.0.2", 14 | "react-scripts": "4.0.3", 15 | "web-vitals": "^1.1.1" 16 | }, 17 | "scripts": { 18 | "start": "react-app-rewired start", 19 | "build": "react-app-rewired build", 20 | "test": "react-app-rewired test", 21 | "eject": "react-app-rewired eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/react/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/react/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-header { 6 | background-color: #222; 7 | height: 150px; 8 | padding: 20px; 9 | color: white; 10 | } 11 | 12 | .App-title { 13 | font-size: 1.5em; 14 | } 15 | 16 | .App-intro { 17 | font-size: large; 18 | } 19 | 20 | .login-wrapper { 21 | display: flex; 22 | flex-direction: column; 23 | align-items: center; 24 | } 25 | -------------------------------------------------------------------------------- /examples/react/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import "./App.css"; 3 | const argon2 = require('argon2-browser') 4 | 5 | function App() { 6 | const [passwd, setPassword] = useState(); 7 | const [salt, setSalt] = useState(); 8 | const [timeCost, setTime] = useState(3); 9 | const [memory, setMemory] = useState(4096); 10 | const [thread, setThread] = useState(1); 11 | const [hashLen, setHashLen] = useState(32); 12 | 13 | const handleSubmit = async e => { 14 | e.preventDefault(); 15 | argon2.hash({ pass: passwd, salt: salt, time: timeCost, mem: memory, parallelism: thread, 16 | hashLen: hashLen, type: argon2.ArgonType.Argon2id}) 17 | .then(h => console.log(h.hash, h.hashHex, h.encoded)) 18 | .catch(e => console.error(e.message, e.code)) 19 | } 20 | return ( 21 |
22 |
23 | 27 | 31 | 35 | 39 | 43 | 47 |
48 | 49 |
50 |
51 |
52 | ); 53 | } 54 | 55 | export default App; 56 | -------------------------------------------------------------------------------- /examples/react/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /examples/react/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /examples/vanilla/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Argon2 Browser VanillaJS Demo 6 | 7 | 8 | 9 |

Argon2-Browser WebPack Demo

10 | 11 |
Output should appear here. If not, please check DevTools in your browser.
12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/vanilla/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vanilla", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "run.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "private": true, 12 | "dependencies": { 13 | "argon2-browser": "^1.13.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/vanilla/run.js: -------------------------------------------------------------------------------- 1 | argon2 2 | .hash({ 3 | pass: 'p@ssw0rd', 4 | salt: 'somesalt' 5 | }) 6 | .then(hash => { 7 | document.querySelector('pre').innerText = 8 | `Encoded: ${hash.encoded}\n` + 9 | `Hex: ${hash.hashHex}\n`; 10 | 11 | argon2 12 | .verify({ 13 | pass: 'p@ssw0rd', 14 | encoded: hash.encoded 15 | }) 16 | .then(() => document.querySelector('pre').innerText += 'Verified OK') 17 | .catch(e => console.error('Error: ', e)); 18 | }) 19 | .catch(e => console.error('Error: ', e)); 20 | -------------------------------------------------------------------------------- /examples/webpack/argon2-demo-webpack.js: -------------------------------------------------------------------------------- 1 | const argon2 = require('argon2-browser'); 2 | 3 | argon2 4 | .hash({ 5 | pass: 'p@ssw0rd', 6 | salt: 'somesalt' 7 | }) 8 | .then(hash => { 9 | document.querySelector('pre').innerText = 10 | `Encoded: ${hash.encoded}\n` + 11 | `Hex: ${hash.hashHex}\n`; 12 | 13 | argon2 14 | .verify({ 15 | pass: 'p@ssw0rd', 16 | encoded: hash.encoded 17 | }) 18 | .then(() => document.querySelector('pre').innerText += 'Verified OK') 19 | .catch(e => console.error('Error: ', e)); 20 | }) 21 | .catch(e => console.error('Error: ', e)); 22 | -------------------------------------------------------------------------------- /examples/webpack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Argon2-Browser WebPack Demo 6 | 7 | 8 |

Argon2-Browser WebPack Demo

9 | 10 |
Output should appear here. If not, please check DevTools in your browser.
11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "argon2-webpack", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "argon2-demo-webpack.js", 7 | "scripts": { 8 | "start": "webpack", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "MIT", 13 | "dependencies": { 14 | "argon2-browser": "^1.15.3", 15 | "base64-loader": "^1.0.0", 16 | "webpack": "^5.10.1" 17 | }, 18 | "devDependencies": { 19 | "webpack-cli": "^4.2.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | // WebPack has native support for WebAssembly modules, however it's not ideal 4 | // and in our case it's introducing more issues than it solves. 5 | // 6 | // When building your own config, please make sure important bits 7 | // of this configuration are included there, otherwise you're likely to 8 | // see the same errors. 9 | // 10 | // If don't include these hacks and it still compiles successfully, 11 | // it means that you're using a more modern version of WebPack. 12 | // I'd be happy if you let me know that it's no longer needed! 13 | module.exports = { 14 | mode: 'production', 15 | entry: './argon2-demo-webpack.js', 16 | output: { 17 | path: path.resolve(__dirname, 'dist'), 18 | publicPath: 'dist/', 19 | filename: 'bundle.js', 20 | }, 21 | module: { 22 | // Makes WebPack think that we don't need to parse this module, 23 | // otherwise it tries to recompile it, but fails 24 | // 25 | // Error: Module not found: Error: Can't resolve 'env' 26 | noParse: /\.wasm$/, 27 | rules: [ 28 | { 29 | test: /\.wasm$/, 30 | // Tells WebPack that this module should be included as 31 | // base64-encoded binary file and not as code 32 | loader: 'base64-loader', 33 | // Disables WebPack's opinion where WebAssembly should be, 34 | // makes it think that it's not WebAssembly 35 | // 36 | // Error: WebAssembly module is included in initial chunk. 37 | type: 'javascript/auto', 38 | }, 39 | ], 40 | }, 41 | resolve: { 42 | // We're using different node.js modules in our code, 43 | // this prevents WebPack from failing on them or embedding 44 | // polyfills for them into the bundle. 45 | // 46 | // Error: Module not found: Error: Can't resolve 'fs' 47 | fallback: { 48 | path: false, 49 | fs: false, 50 | Buffer: false, 51 | process: false, 52 | }, 53 | }, 54 | }; 55 | -------------------------------------------------------------------------------- /lib/argon2.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define([], factory); 4 | } else if (typeof module === 'object' && module.exports) { 5 | module.exports = factory(); 6 | } else { 7 | root.argon2 = factory(); 8 | } 9 | })(typeof self !== 'undefined' ? self : this, function () { 10 | const global = typeof self !== 'undefined' ? self : this; 11 | 12 | /** 13 | * @enum 14 | */ 15 | const ArgonType = { 16 | Argon2d: 0, 17 | Argon2i: 1, 18 | Argon2id: 2, 19 | }; 20 | 21 | function loadModule(mem) { 22 | if (loadModule._promise) { 23 | return loadModule._promise; 24 | } 25 | if (loadModule._module) { 26 | return Promise.resolve(loadModule._module); 27 | } 28 | let promise; 29 | if ( 30 | global.process && 31 | global.process.versions && 32 | global.process.versions.node 33 | ) { 34 | promise = loadWasmModule().then( 35 | (Module) => 36 | new Promise((resolve) => { 37 | Module.postRun = () => resolve(Module); 38 | }) 39 | ); 40 | } else { 41 | promise = loadWasmBinary().then((wasmBinary) => { 42 | const wasmMemory = mem ? createWasmMemory(mem) : undefined; 43 | return initWasm(wasmBinary, wasmMemory); 44 | }); 45 | } 46 | loadModule._promise = promise; 47 | return promise.then((Module) => { 48 | loadModule._module = Module; 49 | delete loadModule._promise; 50 | return Module; 51 | }); 52 | } 53 | 54 | function initWasm(wasmBinary, wasmMemory) { 55 | return new Promise((resolve) => { 56 | global.Module = { 57 | wasmBinary, 58 | wasmMemory, 59 | postRun() { 60 | resolve(Module); 61 | }, 62 | }; 63 | return loadWasmModule(); 64 | }); 65 | } 66 | 67 | function loadWasmModule() { 68 | if (global.loadArgon2WasmModule) { 69 | return global.loadArgon2WasmModule(); 70 | } 71 | if (typeof require === 'function') { 72 | return Promise.resolve(require('../dist/argon2.js')); 73 | } 74 | return import('../dist/argon2.js'); 75 | } 76 | 77 | function loadWasmBinary() { 78 | if (global.loadArgon2WasmBinary) { 79 | return global.loadArgon2WasmBinary(); 80 | } 81 | if (typeof require === 'function') { 82 | return Promise.resolve(require('../dist/argon2.wasm')).then( 83 | (wasmModule) => { 84 | return decodeWasmBinary(wasmModule); 85 | } 86 | ); 87 | } 88 | const wasmPath = 89 | global.argon2WasmPath || 90 | 'node_modules/argon2-browser/dist/argon2.wasm'; 91 | return fetch(wasmPath) 92 | .then((response) => response.arrayBuffer()) 93 | .then((ab) => new Uint8Array(ab)); 94 | } 95 | 96 | function decodeWasmBinary(base64) { 97 | if (typeof Buffer === 'function') { 98 | return new Uint8Array(Buffer.from(base64, 'base64')); 99 | } 100 | const text = atob(base64); 101 | const binary = new Uint8Array(new ArrayBuffer(text.length)); 102 | for (let i = 0; i < text.length; i++) { 103 | binary[i] = text.charCodeAt(i); 104 | } 105 | return binary; 106 | } 107 | 108 | function createWasmMemory(mem) { 109 | const KB = 1024; 110 | const MB = 1024 * KB; 111 | const GB = 1024 * MB; 112 | const WASM_PAGE_SIZE = 64 * KB; 113 | 114 | const totalMemory = (2 * GB - 64 * KB) / WASM_PAGE_SIZE; 115 | const initialMemory = Math.min( 116 | Math.max(Math.ceil((mem * KB) / WASM_PAGE_SIZE), 256) + 256, 117 | totalMemory 118 | ); 119 | 120 | return new WebAssembly.Memory({ 121 | initial: initialMemory, 122 | maximum: totalMemory, 123 | }); 124 | } 125 | 126 | function allocateArray(Module, arr) { 127 | return Module.allocate(arr, 'i8', Module.ALLOC_NORMAL); 128 | } 129 | 130 | function allocateArrayStr(Module, arr) { 131 | const nullTerminatedArray = new Uint8Array([...arr, 0]); 132 | return allocateArray(Module, nullTerminatedArray); 133 | } 134 | 135 | function encodeUtf8(str) { 136 | if (typeof str !== 'string') { 137 | return str; 138 | } 139 | if (typeof TextEncoder === 'function') { 140 | return new TextEncoder().encode(str); 141 | } else if (typeof Buffer === 'function') { 142 | return Buffer.from(str); 143 | } else { 144 | throw new Error("Don't know how to encode UTF8"); 145 | } 146 | } 147 | 148 | /** 149 | * Argon2 hash 150 | * @param {string|Uint8Array} params.pass - password string 151 | * @param {string|Uint8Array} params.salt - salt string 152 | * @param {number} [params.time=1] - the number of iterations 153 | * @param {number} [params.mem=1024] - used memory, in KiB 154 | * @param {number} [params.hashLen=24] - desired hash length 155 | * @param {number} [params.parallelism=1] - desired parallelism 156 | * @param {number} [params.type=argon2.ArgonType.Argon2d] - hash type: 157 | * argon2.ArgonType.Argon2d 158 | * argon2.ArgonType.Argon2i 159 | * argon2.ArgonType.Argon2id 160 | * 161 | * @return Promise 162 | * 163 | * @example 164 | * argon2.hash({ pass: 'password', salt: 'somesalt' }) 165 | * .then(h => console.log(h.hash, h.hashHex, h.encoded)) 166 | * .catch(e => console.error(e.message, e.code)) 167 | */ 168 | function argon2Hash(params) { 169 | const mCost = params.mem || 1024; 170 | return loadModule(mCost).then((Module) => { 171 | const tCost = params.time || 1; 172 | const parallelism = params.parallelism || 1; 173 | const pwdEncoded = encodeUtf8(params.pass); 174 | const pwd = allocateArrayStr(Module, pwdEncoded); 175 | const pwdlen = pwdEncoded.length; 176 | const saltEncoded = encodeUtf8(params.salt); 177 | const salt = allocateArrayStr(Module, saltEncoded); 178 | const saltlen = saltEncoded.length; 179 | const argon2Type = params.type || ArgonType.Argon2d; 180 | const hash = Module.allocate( 181 | new Array(params.hashLen || 24), 182 | 'i8', 183 | Module.ALLOC_NORMAL 184 | ); 185 | const secret = params.secret 186 | ? allocateArray(Module, params.secret) 187 | : 0; 188 | const secretlen = params.secret ? params.secret.byteLength : 0; 189 | const ad = params.ad ? allocateArray(Module, params.ad) : 0; 190 | const adlen = params.ad ? params.ad.byteLength : 0; 191 | const hashlen = params.hashLen || 24; 192 | const encodedlen = Module._argon2_encodedlen( 193 | tCost, 194 | mCost, 195 | parallelism, 196 | saltlen, 197 | hashlen, 198 | argon2Type 199 | ); 200 | const encoded = Module.allocate( 201 | new Array(encodedlen + 1), 202 | 'i8', 203 | Module.ALLOC_NORMAL 204 | ); 205 | const version = 0x13; 206 | let err; 207 | let res; 208 | try { 209 | res = Module._argon2_hash_ext( 210 | tCost, 211 | mCost, 212 | parallelism, 213 | pwd, 214 | pwdlen, 215 | salt, 216 | saltlen, 217 | hash, 218 | hashlen, 219 | encoded, 220 | encodedlen, 221 | argon2Type, 222 | secret, 223 | secretlen, 224 | ad, 225 | adlen, 226 | version 227 | ); 228 | } catch (e) { 229 | err = e; 230 | } 231 | let result; 232 | if (res === 0 && !err) { 233 | let hashStr = ''; 234 | const hashArr = new Uint8Array(hashlen); 235 | for (let i = 0; i < hashlen; i++) { 236 | const byte = Module.HEAP8[hash + i]; 237 | hashArr[i] = byte; 238 | hashStr += ('0' + (0xff & byte).toString(16)).slice(-2); 239 | } 240 | const encodedStr = Module.UTF8ToString(encoded); 241 | result = { 242 | hash: hashArr, 243 | hashHex: hashStr, 244 | encoded: encodedStr, 245 | }; 246 | } else { 247 | try { 248 | if (!err) { 249 | err = Module.UTF8ToString( 250 | Module._argon2_error_message(res) 251 | ); 252 | } 253 | } catch (e) {} 254 | result = { message: err, code: res }; 255 | } 256 | try { 257 | Module._free(pwd); 258 | Module._free(salt); 259 | Module._free(hash); 260 | Module._free(encoded); 261 | if (ad) { 262 | Module._free(ad); 263 | } 264 | if (secret) { 265 | Module._free(secret); 266 | } 267 | } catch (e) {} 268 | if (err) { 269 | throw result; 270 | } else { 271 | return result; 272 | } 273 | }); 274 | } 275 | 276 | /** 277 | * Argon2 verify function 278 | * @param {string} params.pass - password string 279 | * @param {string|Uint8Array} params.encoded - encoded hash 280 | * @param {number} [params.type=argon2.ArgonType.Argon2d] - hash type: 281 | * argon2.ArgonType.Argon2d 282 | * argon2.ArgonType.Argon2i 283 | * argon2.ArgonType.Argon2id 284 | * 285 | * @returns Promise 286 | * 287 | * @example 288 | * argon2.verify({ pass: 'password', encoded: 'encoded-hash' }) 289 | * .then(() => console.log('OK')) 290 | * .catch(e => console.error(e.message, e.code)) 291 | */ 292 | function argon2Verify(params) { 293 | return loadModule().then((Module) => { 294 | const pwdEncoded = encodeUtf8(params.pass); 295 | const pwd = allocateArrayStr(Module, pwdEncoded); 296 | const pwdlen = pwdEncoded.length; 297 | const secret = params.secret 298 | ? allocateArray(Module, params.secret) 299 | : 0; 300 | const secretlen = params.secret ? params.secret.byteLength : 0; 301 | const ad = params.ad ? allocateArray(Module, params.ad) : 0; 302 | const adlen = params.ad ? params.ad.byteLength : 0; 303 | const encEncoded = encodeUtf8(params.encoded); 304 | const enc = allocateArrayStr(Module, encEncoded); 305 | let argon2Type = params.type; 306 | if (argon2Type === undefined) { 307 | let typeStr = params.encoded.split('$')[1]; 308 | if (typeStr) { 309 | typeStr = typeStr.replace('a', 'A'); 310 | argon2Type = ArgonType[typeStr] || ArgonType.Argon2d; 311 | } 312 | } 313 | let err; 314 | let res; 315 | try { 316 | res = Module._argon2_verify_ext( 317 | enc, 318 | pwd, 319 | pwdlen, 320 | secret, 321 | secretlen, 322 | ad, 323 | adlen, 324 | argon2Type 325 | ); 326 | } catch (e) { 327 | err = e; 328 | } 329 | let result; 330 | if (res || err) { 331 | try { 332 | if (!err) { 333 | err = Module.UTF8ToString( 334 | Module._argon2_error_message(res) 335 | ); 336 | } 337 | } catch (e) {} 338 | result = { message: err, code: res }; 339 | } 340 | try { 341 | Module._free(pwd); 342 | Module._free(enc); 343 | } catch (e) {} 344 | if (err) { 345 | throw result; 346 | } else { 347 | return result; 348 | } 349 | }); 350 | } 351 | 352 | function unloadRuntime() { 353 | if (loadModule._module) { 354 | loadModule._module.unloadRuntime(); 355 | delete loadModule._promise; 356 | delete loadModule._module; 357 | } 358 | } 359 | 360 | return { 361 | ArgonType, 362 | hash: argon2Hash, 363 | verify: argon2Verify, 364 | unloadRuntime, 365 | }; 366 | }); 367 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "argon2-browser", 3 | "version": "1.18.0", 4 | "description": "Argon2 library compiled for browser runtime", 5 | "main": "lib/argon2.js", 6 | "directories": { 7 | "doc": "docs", 8 | "lib": "lib" 9 | }, 10 | "scripts": { 11 | "test": "mocha", 12 | "build-bundle": "webpack" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/antelle/argon2-browser.git" 17 | }, 18 | "author": "Antelle", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/antelle/argon2-browser/issues" 22 | }, 23 | "homepage": "https://github.com/antelle/argon2-browser#readme", 24 | "devDependencies": { 25 | "base64-loader": "^1.0.0", 26 | "chai": "^4.3.4", 27 | "http-server": "^0.12.3", 28 | "mocha": "^8.4.0", 29 | "prettier": "^2.3.0", 30 | "puppeteer": "^9.1.1", 31 | "webpack": "^5.37.1", 32 | "webpack-cli": "^4.7.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /perf-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | uint64_t fBlaMka(uint64_t x, uint64_t y) { 4 | const uint64_t m = UINT64_C(0xFFFFFFFF); 5 | const uint64_t xy = (x & m) * (y & m); 6 | return x + y + 2 * xy; 7 | } 8 | 9 | 10 | int exp_fBlaMka() { 11 | for (unsigned i = 0; i < 100000000; i++) { 12 | if (fBlaMka(i, i) == i - 1) { 13 | return 1; 14 | } 15 | } 16 | return 0; 17 | } 18 | 19 | // console.time('fBlaMka'); 20 | // Module._exp_fBlaMka(); 21 | // console.timeEnd('fBlaMka'); 22 | 23 | // i32: 1851.5ms 24 | // i64: 414.49ms -------------------------------------------------------------------------------- /src/argon2_library.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../argon2/include/argon2.h" 6 | #include "../argon2/src/encoding.h" 7 | #include "../argon2/src/core.h" 8 | 9 | // Copied from argon2/src/argon2.c with additional parameters: "secret"+len and "ad"+len 10 | 11 | int argon2_hash_ext(const uint32_t t_cost, const uint32_t m_cost, 12 | const uint32_t parallelism, const void *pwd, 13 | const size_t pwdlen, const void *salt, const size_t saltlen, 14 | void *hash, const size_t hashlen, char *encoded, 15 | const size_t encodedlen, argon2_type type, 16 | /* argon2-browser: begin added parameters */ 17 | const void* secret, const size_t secretlen, 18 | const void* ad, const size_t adlen, 19 | /* argon2-browser: end added parameters */ 20 | const uint32_t version){ 21 | 22 | argon2_context context; 23 | int result; 24 | uint8_t *out; 25 | 26 | if (pwdlen > ARGON2_MAX_PWD_LENGTH) { 27 | return ARGON2_PWD_TOO_LONG; 28 | } 29 | 30 | if (saltlen > ARGON2_MAX_SALT_LENGTH) { 31 | return ARGON2_SALT_TOO_LONG; 32 | } 33 | 34 | if (hashlen > ARGON2_MAX_OUTLEN) { 35 | return ARGON2_OUTPUT_TOO_LONG; 36 | } 37 | 38 | if (hashlen < ARGON2_MIN_OUTLEN) { 39 | return ARGON2_OUTPUT_TOO_SHORT; 40 | } 41 | 42 | out = malloc(hashlen); 43 | if (!out) { 44 | return ARGON2_MEMORY_ALLOCATION_ERROR; 45 | } 46 | 47 | context.out = (uint8_t *)out; 48 | context.outlen = (uint32_t)hashlen; 49 | context.pwd = CONST_CAST(uint8_t *)pwd; 50 | context.pwdlen = (uint32_t)pwdlen; 51 | context.salt = CONST_CAST(uint8_t *)salt; 52 | context.saltlen = (uint32_t)saltlen; 53 | /* argon2-browser: begin changed parameters */ 54 | context.secret = CONST_CAST(uint8_t *)secret; 55 | context.secretlen = (uint32_t)secretlen; 56 | context.ad = CONST_CAST(uint8_t *)ad; 57 | context.adlen = (uint32_t)adlen; 58 | /* argon2-browser: end changed parameters */ 59 | context.t_cost = t_cost; 60 | context.m_cost = m_cost; 61 | context.lanes = parallelism; 62 | context.threads = parallelism; 63 | context.allocate_cbk = NULL; 64 | context.free_cbk = NULL; 65 | context.flags = ARGON2_DEFAULT_FLAGS; 66 | context.version = version; 67 | 68 | result = argon2_ctx(&context, type); 69 | 70 | if (result != ARGON2_OK) { 71 | clear_internal_memory(out, hashlen); 72 | free(out); 73 | return result; 74 | } 75 | 76 | /* if raw hash requested, write it */ 77 | if (hash) { 78 | memcpy(hash, out, hashlen); 79 | } 80 | 81 | /* if encoding requested, write it */ 82 | if (encoded && encodedlen) { 83 | if (encode_string(encoded, encodedlen, &context, type) != ARGON2_OK) { 84 | clear_internal_memory(out, hashlen); /* wipe buffers if error */ 85 | clear_internal_memory(encoded, encodedlen); 86 | free(out); 87 | return ARGON2_ENCODING_FAIL; 88 | } 89 | } 90 | clear_internal_memory(out, hashlen); 91 | free(out); 92 | 93 | return ARGON2_OK; 94 | } 95 | 96 | int argon2_verify_ext(const char *encoded, const void *pwd, const size_t pwdlen, 97 | /* argon2-browser: begin added parameters */ 98 | const void* secret, const size_t secretlen, 99 | const void* ad, const size_t adlen, 100 | /* argon2-browser: end added parameters */ 101 | argon2_type type) { 102 | 103 | argon2_context ctx; 104 | uint8_t *desired_result = NULL; 105 | 106 | int ret = ARGON2_OK; 107 | 108 | size_t encoded_len; 109 | uint32_t max_field_len; 110 | 111 | if (pwdlen > ARGON2_MAX_PWD_LENGTH) { 112 | return ARGON2_PWD_TOO_LONG; 113 | } 114 | 115 | if (encoded == NULL) { 116 | return ARGON2_DECODING_FAIL; 117 | } 118 | 119 | encoded_len = strlen(encoded); 120 | if (encoded_len > UINT32_MAX) { 121 | return ARGON2_DECODING_FAIL; 122 | } 123 | 124 | /* No field can be longer than the encoded length */ 125 | max_field_len = (uint32_t)encoded_len; 126 | 127 | ctx.saltlen = max_field_len; 128 | ctx.outlen = max_field_len; 129 | 130 | ctx.salt = malloc(ctx.saltlen); 131 | ctx.out = malloc(ctx.outlen); 132 | if (!ctx.salt || !ctx.out) { 133 | ret = ARGON2_MEMORY_ALLOCATION_ERROR; 134 | goto fail; 135 | } 136 | 137 | ctx.pwd = (uint8_t *)pwd; 138 | ctx.pwdlen = (uint32_t)pwdlen; 139 | 140 | ret = decode_string(&ctx, encoded, type); 141 | if (ret != ARGON2_OK) { 142 | goto fail; 143 | } 144 | 145 | /* Set aside the desired result, and get a new buffer. */ 146 | desired_result = ctx.out; 147 | ctx.out = malloc(ctx.outlen); 148 | if (!ctx.out) { 149 | ret = ARGON2_MEMORY_ALLOCATION_ERROR; 150 | goto fail; 151 | } 152 | 153 | /* argon2-browser: begin changed parameters */ 154 | ctx.secret = CONST_CAST(uint8_t *)secret; 155 | ctx.secretlen = (uint32_t)secretlen; 156 | ctx.ad = CONST_CAST(uint8_t *)ad; 157 | ctx.adlen = (uint32_t)adlen; 158 | /* argon2-browser: end changed parameters */ 159 | 160 | ret = argon2_verify_ctx(&ctx, (char *)desired_result, type); 161 | if (ret != ARGON2_OK) { 162 | goto fail; 163 | } 164 | 165 | fail: 166 | free(ctx.salt); 167 | free(ctx.out); 168 | free(desired_result); 169 | 170 | return ret; 171 | } 172 | -------------------------------------------------------------------------------- /test/browser.js: -------------------------------------------------------------------------------- 1 | describe('Browser', function() { 2 | const path = require('path'); 3 | const child_process = require('child_process'); 4 | const assert = require('chai').assert; 5 | const puppeteer = require('puppeteer'); 6 | const httpServer = require('http-server'); 7 | 8 | const port = 19273; 9 | const server = httpServer.createServer({ 10 | root: path.resolve(__dirname, '..') 11 | }); 12 | 13 | let browser; 14 | 15 | before(function(done) { 16 | this.timeout(60000); 17 | (async function() { 18 | browser = await puppeteer.launch(); 19 | server.listen(port); 20 | rmDist(); 21 | done(); 22 | })(); 23 | }); 24 | 25 | after(function(done) { 26 | (async function() { 27 | await browser.close(); 28 | server.close(); 29 | rmDist(); 30 | done(); 31 | })(); 32 | }); 33 | 34 | it('should run tests in browser using vanilla javascript', async function() { 35 | this.timeout(60000); 36 | return await runInBrowser('vanilla'); 37 | }); 38 | 39 | it('should run tests in browser using webpack', async function() { 40 | this.timeout(120000); 41 | 42 | const options = { 43 | cwd: path.resolve(__dirname, 'webpack'), 44 | stdio: 'inherit' 45 | }; 46 | 47 | let result = child_process.spawnSync('npm', ['i'], options); 48 | assert.strictEqual(result.status, 0, 'Installing npm modules'); 49 | 50 | result = child_process.spawnSync('npm', ['start'], options); 51 | assert.strictEqual(result.status, 0, 'Building javascript'); 52 | 53 | return await runInBrowser('webpack'); 54 | }); 55 | 56 | async function runInBrowser(htmlFolder) { 57 | const page = await browser.newPage(); 58 | await page.goto( 59 | `http://localhost:${port}/test/${htmlFolder}/index.html` 60 | ); 61 | await page.waitFor(() => window.argon2testResults); 62 | const result = await page.evaluate(() => window.argon2testResults); 63 | 64 | assert.ok(result); 65 | assert.ok(result.stdout); 66 | assert.ok(result.stderr); 67 | assert.strictEqual(result.stderr.length, 0); 68 | 69 | const stdout = result.stdout.join('').trim(); 70 | assert.ok(stdout); 71 | 72 | const report = JSON.parse(stdout); 73 | 74 | assert.ok(report); 75 | assert.ok(report.stats); 76 | assert.ok(report.stats.suites); 77 | assert.ok(report.stats.passes); 78 | assert.strictEqual(report.stats.pending, 0); 79 | assert.strictEqual(report.stats.failures, 0); 80 | } 81 | 82 | function rmDist() { 83 | child_process.spawnSync('rm', [ 84 | '-rf', 85 | path.resolve(__dirname, 'webpack', 'dist') 86 | ]); 87 | } 88 | }); 89 | -------------------------------------------------------------------------------- /test/node.js: -------------------------------------------------------------------------------- 1 | describe('Node.js', function() { 2 | global.chai = require('chai'); 3 | global.argon2 = require('..'); 4 | 5 | require('./suite/hash'); 6 | require('./suite/verify'); 7 | }); 8 | -------------------------------------------------------------------------------- /test/suite/hash.js: -------------------------------------------------------------------------------- 1 | describe('argon2.hash', function () { 2 | const { assert } = chai; 3 | 4 | const hash_data = it('should compute hash', async function () { 5 | const hash = await argon2.hash({ 6 | pass: 'p@ssw0rd', 7 | salt: 'somesalt', 8 | }); 9 | assert.ok(hash); 10 | assert.deepStrictEqual( 11 | hash.hash, 12 | /* prettier-ignore */ 13 | new Uint8Array([36, 99, 163, 35, 223, 30, 71, 14, 26, 134, 8, 54, 14 | 243, 110, 116, 23, 61, 129, 40, 65, 101, 227, 197, 230]) 15 | ); 16 | assert.strictEqual( 17 | hash.hashHex, 18 | '2463a323df1e470e1a860836f36e74173d81284165e3c5e6' 19 | ); 20 | assert.strictEqual( 21 | hash.encoded, 22 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$JGOjI98eRw4ahgg28250Fz2BKEFl48Xm' 23 | ); 24 | }); 25 | 26 | it('should be able to work several times', async function () { 27 | let hash = await argon2.hash({ 28 | pass: 'p@ssw0rd', 29 | salt: 'somesalt', 30 | }); 31 | assert.ok(hash); 32 | assert.strictEqual( 33 | hash.hashHex, 34 | '2463a323df1e470e1a860836f36e74173d81284165e3c5e6' 35 | ); 36 | assert.strictEqual( 37 | hash.encoded, 38 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$JGOjI98eRw4ahgg28250Fz2BKEFl48Xm' 39 | ); 40 | 41 | hash = await argon2.hash({ 42 | pass: 'p@ssw0rd2', 43 | salt: 'somesalt', 44 | }); 45 | assert.ok(hash); 46 | assert.strictEqual( 47 | hash.hashHex, 48 | 'f02360855fe22752362451bcd41206041304ce7908b5645b' 49 | ); 50 | assert.strictEqual( 51 | hash.encoded, 52 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$8CNghV/iJ1I2JFG81BIGBBMEznkItWRb' 53 | ); 54 | }); 55 | 56 | it('should compute hash for Argon2d', async function () { 57 | const hash = await argon2.hash({ 58 | pass: 'p@ssw0rd', 59 | salt: 'somesalt', 60 | type: argon2.ArgonType.Argon2d, 61 | }); 62 | assert.strictEqual( 63 | hash.encoded, 64 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$JGOjI98eRw4ahgg28250Fz2BKEFl48Xm' 65 | ); 66 | }); 67 | 68 | it('should compute hash for Argon2i', async function () { 69 | const hash = await argon2.hash({ 70 | pass: 'p@ssw0rd', 71 | salt: 'somesalt', 72 | type: argon2.ArgonType.Argon2i, 73 | }); 74 | assert.strictEqual( 75 | hash.encoded, 76 | '$argon2i$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$I4odzyBxoIvAnqjao5xt4/xS02zts+Jb' 77 | ); 78 | }); 79 | 80 | it('should compute hash for Argon2id', async function () { 81 | const hash = await argon2.hash({ 82 | pass: 'p@ssw0rd', 83 | salt: 'somesalt', 84 | type: argon2.ArgonType.Argon2id, 85 | }); 86 | assert.strictEqual( 87 | hash.encoded, 88 | '$argon2id$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$dF4LZGqnznPWATUG/6m1Yp/Id1nKVSlk' 89 | ); 90 | }); 91 | 92 | it('should compute hash with different time complexity', async function () { 93 | const hash = await argon2.hash({ 94 | pass: 'p@ssw0rd', 95 | salt: 'somesalt', 96 | time: 10, 97 | }); 98 | assert.strictEqual( 99 | hash.encoded, 100 | '$argon2d$v=19$m=1024,t=10,p=1$c29tZXNhbHQ$uo6bzQ2Wjb9LhxGoSHnaMrIOAB/6dGAS' 101 | ); 102 | }); 103 | 104 | it('should compute hash with different memory complexity', async function () { 105 | const hash = await argon2.hash({ 106 | pass: 'p@ssw0rd', 107 | salt: 'somesalt', 108 | mem: 2048, 109 | }); 110 | assert.strictEqual( 111 | hash.encoded, 112 | '$argon2d$v=19$m=2048,t=1,p=1$c29tZXNhbHQ$6OXzaE6bdwMuoUqJt5U1TCrfJBF/8X3O' 113 | ); 114 | }); 115 | 116 | it('should compute hash with 1GB memory', async function () { 117 | this.timeout(30000); 118 | 119 | const hash = await argon2.hash({ 120 | pass: 'p@ssw0rd', 121 | salt: 'somesalt', 122 | mem: 1024 * 1024, 123 | }); 124 | assert.strictEqual( 125 | hash.encoded, 126 | '$argon2d$v=19$m=1048576,t=1,p=1$c29tZXNhbHQ$pqFpxcC+pJwEsSc8+YIkg5pnrNye8EDt' 127 | ); 128 | }); 129 | 130 | it('should compute hash with different length', async function () { 131 | const hash = await argon2.hash({ 132 | pass: 'p@ssw0rd', 133 | salt: 'somesalt', 134 | hashLen: 32, 135 | }); 136 | assert.strictEqual( 137 | hash.encoded, 138 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$3w1C/UXK5b+K9eGlhctsuw1TivVU1JFCmB+DmM+MIiY' 139 | ); 140 | }); 141 | 142 | it('should compute hash with different parallelism', async function () { 143 | const hash = await argon2.hash({ 144 | pass: 'p@ssw0rd', 145 | salt: 'somesalt', 146 | parallelism: 4, 147 | }); 148 | assert.strictEqual( 149 | hash.encoded, 150 | '$argon2d$v=19$m=1024,t=1,p=4$c29tZXNhbHQ$0/nLmgLRIKqzzOET6/YYHgZrp0xJjACg' 151 | ); 152 | }); 153 | 154 | it('should compute hash of a string with unicode characters', async function () { 155 | const hash = await argon2.hash({ 156 | pass: '汉字漢字', 157 | salt: 'asdfasdfasdfasdf', 158 | type: argon2.ArgonType.Argon2id, 159 | hashLen: 32, 160 | }); 161 | assert.strictEqual( 162 | hash.encoded, 163 | '$argon2id$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$zzqgQLEjqikDwII1Qk2ZbyoCG12D25W7tXSgejiwiS0' 164 | ); 165 | }); 166 | 167 | it('should compute hash with additional data', async function () { 168 | const hash = await argon2.hash({ 169 | pass: 'p@ssw0rd', 170 | salt: 'somesalt', 171 | ad: new Uint8Array([1, 2, 3]), 172 | }); 173 | assert.strictEqual( 174 | hash.encoded, 175 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$CuATwqimHlFHq0mTT0qnozY4kCjrGg7X' 176 | ); 177 | }); 178 | 179 | it('should compute hash with a secret', async function () { 180 | const hash = await argon2.hash({ 181 | pass: 'p@ssw0rd', 182 | salt: 'somesalt', 183 | secret: new Uint8Array([1, 2, 3]), 184 | }); 185 | assert.strictEqual( 186 | hash.encoded, 187 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$kPOHk/DlE6du1nkbKKom8FV+fcNjviLW' 188 | ); 189 | }); 190 | 191 | it('should compute long hashes', async function () { 192 | const hash = await argon2.hash({ 193 | pass: 'p@ssw0rd', 194 | salt: 'somesalt', 195 | hashLen: 100000, 196 | }); 197 | assert.match( 198 | hash.encoded, 199 | /^\$argon2d\$v=19\$m=1024,t=1,p=1\$c29tZXNhbHQ\$A0myEnizYlgEZQ7gUkfNESi8\/.*\/2Zy0G7fzjEl\/bKA$/ 200 | ); 201 | }); 202 | }); 203 | -------------------------------------------------------------------------------- /test/suite/verify.js: -------------------------------------------------------------------------------- 1 | describe('argon2.verify', function () { 2 | const { assert } = chai; 3 | 4 | it('should verify a hash', async function () { 5 | await argon2.verify({ 6 | pass: 'p@ssw0rd', 7 | encoded: 8 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$JGOjI98eRw4ahgg28250Fz2BKEFl48Xm', 9 | }); 10 | }); 11 | 12 | it('should throw an error for invalid hash', async function () { 13 | let error = undefined; 14 | await argon2 15 | .verify({ 16 | pass: 'p@ssw0rd2', 17 | encoded: 18 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$JGOjI98eRw4ahgg28250Fz2BKEFl48Xm', 19 | }) 20 | .catch((e) => (error = e)); 21 | assert.ok(error); 22 | assert.ok(error.code); 23 | assert.strictEqual( 24 | error.message, 25 | 'The password does not match the supplied hash' 26 | ); 27 | }); 28 | 29 | it('should be able to work several times', async function () { 30 | await argon2.verify({ 31 | pass: 'p@ssw0rd', 32 | encoded: 33 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$JGOjI98eRw4ahgg28250Fz2BKEFl48Xm', 34 | }); 35 | await argon2.verify({ 36 | pass: 'p@ssw0rd', 37 | encoded: 38 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$JGOjI98eRw4ahgg28250Fz2BKEFl48Xm', 39 | }); 40 | }); 41 | 42 | it('should verify Argon2d with type specified', async function () { 43 | await argon2.verify({ 44 | pass: 'p@ssw0rd', 45 | encoded: 46 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$JGOjI98eRw4ahgg28250Fz2BKEFl48Xm', 47 | type: argon2.ArgonType.Argon2d, 48 | }); 49 | }); 50 | 51 | it('should throw an error if invalid type is specified', async function () { 52 | let error = undefined; 53 | await argon2 54 | .verify({ 55 | pass: 'p@ssw0rd', 56 | encoded: 57 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$JGOjI98eRw4ahgg28250Fz2BKEFl48Xm', 58 | type: argon2.ArgonType.Argon2i, 59 | }) 60 | .catch((e) => (error = e)); 61 | assert.ok(error); 62 | assert.strictEqual(error.message, 'Decoding failed'); 63 | }); 64 | 65 | it('should verify Argon2i', async function () { 66 | await argon2.verify({ 67 | pass: 'p@ssw0rd', 68 | encoded: 69 | '$argon2i$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$I4odzyBxoIvAnqjao5xt4/xS02zts+Jb', 70 | }); 71 | }); 72 | 73 | it('should verify Argon2id', async function () { 74 | await argon2.verify({ 75 | pass: 'p@ssw0rd', 76 | encoded: 77 | '$argon2id$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$dF4LZGqnznPWATUG/6m1Yp/Id1nKVSlk', 78 | }); 79 | }); 80 | 81 | it('should verify hash with different time complexity', async function () { 82 | await argon2.verify({ 83 | pass: 'p@ssw0rd', 84 | encoded: 85 | '$argon2d$v=19$m=1024,t=10,p=1$c29tZXNhbHQ$uo6bzQ2Wjb9LhxGoSHnaMrIOAB/6dGAS', 86 | }); 87 | }); 88 | 89 | it('should verify hash with different memory complexity', async function () { 90 | await argon2.verify({ 91 | pass: 'p@ssw0rd', 92 | encoded: 93 | '$argon2d$v=19$m=2048,t=1,p=1$c29tZXNhbHQ$6OXzaE6bdwMuoUqJt5U1TCrfJBF/8X3O', 94 | }); 95 | }); 96 | 97 | it('should verify hash with different length', async function () { 98 | await argon2.verify({ 99 | pass: 'p@ssw0rd', 100 | encoded: 101 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$3w1C/UXK5b+K9eGlhctsuw1TivVU1JFCmB+DmM+MIiY', 102 | }); 103 | }); 104 | 105 | it('should verify hash with different parallelism', async function () { 106 | await argon2.verify({ 107 | pass: 'p@ssw0rd', 108 | encoded: 109 | '$argon2d$v=19$m=1024,t=1,p=4$c29tZXNhbHQ$0/nLmgLRIKqzzOET6/YYHgZrp0xJjACg', 110 | }); 111 | }); 112 | 113 | it('should verify hash with unicode characters', async function () { 114 | await argon2.verify({ 115 | pass: '汉字漢字', 116 | encoded: 117 | '$argon2id$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$zzqgQLEjqikDwII1Qk2ZbyoCG12D25W7tXSgejiwiS0', 118 | }); 119 | }); 120 | 121 | it('should verify a hash with additional data', async function () { 122 | await argon2.verify({ 123 | pass: 'p@ssw0rd', 124 | ad: new Uint8Array([1, 2, 3]), 125 | encoded: 126 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$CuATwqimHlFHq0mTT0qnozY4kCjrGg7X', 127 | }); 128 | }); 129 | 130 | it('should verify a hash with a secret', async function () { 131 | await argon2.verify({ 132 | pass: 'p@ssw0rd', 133 | secret: new Uint8Array([1, 2, 3]), 134 | encoded: 135 | '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$kPOHk/DlE6du1nkbKKom8FV+fcNjviLW', 136 | }); 137 | }); 138 | }); 139 | -------------------------------------------------------------------------------- /test/vanilla/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Argon2 Browser Tests 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 16 | 17 | 20 | 21 | 22 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/webpack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Argon2 Browser Tests 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "argon2-webpack-tests", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "tests.js", 7 | "scripts": { 8 | "start": "webpack", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "MIT", 13 | "dependencies": { 14 | "base64-loader": "^1.0.0", 15 | "webpack": "^5.10.1", 16 | "webpack-cli": "^4.2.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/webpack/tests.js: -------------------------------------------------------------------------------- 1 | mocha.setup('bdd'); 2 | 3 | global.chat = require('chai'); 4 | global.argon2 = require('argon2-browser'); 5 | 6 | require('../suite/hash'); 7 | require('../suite/verify'); 8 | 9 | const stdout = []; 10 | const stderr = []; 11 | 12 | console.log = (...args) => stdout.push(args); 13 | console.info = (...args) => stdout.push(args); 14 | console.error = (...args) => stderr.push(args); 15 | console.warn = (...args) => stderr.push(args); 16 | 17 | mocha.reporter('json'); 18 | mocha.run(() => window.argon2testResults = { stdout, stderr }); 19 | -------------------------------------------------------------------------------- /test/webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | entry: './tests.js', 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | publicPath: 'dist/', 9 | filename: 'bundle.js', 10 | }, 11 | resolve: { 12 | alias: { 13 | 'argon2-browser': path.resolve(__dirname, '../..'), 14 | }, 15 | fallback: { 16 | path: false, 17 | fs: false, 18 | Buffer: false, 19 | process: false, 20 | }, 21 | }, 22 | module: { 23 | noParse: /\.wasm$/, 24 | rules: [ 25 | { 26 | test: /\.wasm$/, 27 | loader: 'base64-loader', 28 | type: 'javascript/auto', 29 | }, 30 | ], 31 | }, 32 | performance: { 33 | hints: false, 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | entry: './lib/argon2.js', 6 | output: { 7 | library: { 8 | name: 'argon2', 9 | type: 'umd', 10 | }, 11 | globalObject: 'this', 12 | path: path.resolve(__dirname, 'dist'), 13 | publicPath: 'dist/', 14 | filename: 'argon2-bundled.min.js', 15 | }, 16 | module: { 17 | noParse: /\.wasm$/, 18 | rules: [ 19 | { 20 | test: /\.wasm$/, 21 | loader: 'base64-loader', 22 | type: 'javascript/auto', 23 | }, 24 | ], 25 | }, 26 | externals: { 27 | path: 'path', 28 | fs: 'fs', 29 | }, 30 | resolve: { 31 | fallback: { 32 | path: false, 33 | fs: false, 34 | Buffer: false, 35 | process: false, 36 | }, 37 | }, 38 | }; 39 | --------------------------------------------------------------------------------