Giter Site home page Giter Site logo

antelle / argon2-browser Goto Github PK

View Code? Open in Web Editor NEW
358.0 9.0 79.0 3.68 MB

Argon2 library compiled for browser runtime

Home Page: https://antelle.net/argon2-browser

License: MIT License

CMake 1.77% Shell 7.25% C 13.87% JavaScript 72.81% HTML 4.29%
argon2 hash wasm webassembly keeweb keepass kdf

argon2-browser's Introduction

Argon2 in browser Build

Argon2 is a password-hashing function, the winner of Password Hashing Competition. Here Argon2 library is compiled for browser runtime.

Live demo

More about Argon2

Usage

The numbers

Time, ms (lower is better)
Chrome WASM 225
Chrome WASM+SIMD 119
Firefox WASM 195
Firefox WASM+SIMD 135
Safari WASM 174
Native -O3 SSE 15
Native -O3 42
Native -O1 55
Native -O0 395

Test Environment

Environment used to get the numbers above:

Algorithm parameters (-d -t 100 -m 10 -p 1):

  • iterations: 100
  • memory: 1MiB (1024 KiB)
  • hash length: 32
  • parallelism: 1
  • argon2d

Environment:

  • MacBook pro 2020, Intel Core i7, 2.3GHz (x64), macOS 10.14.6 (18G95)
  • Chrome 85.0.4183.83 (Official Build)
  • Firefox 80.0.1
  • Safari 13.1.2 (15609.3.5.1.3)
  • native argon2 compiled from https://github.com/P-H-C/phc-winner-argon2 @440ceb9

Code size

ll -h dist

File Code size, kB
argon2.js 14
argon2.wasm 25

Is Argon2 modified?

No, it's used a submodule from upstream.

SIMD

SIMD is not quite here in WebAssembly, however for those who would like to give it a try, we already provide a working build with SIMD. At the moment it works only in Chrome, to be able to use it, you need to either add this origin trial to your website, or enable the SIMD feature in Chrome flags.

More about WebAssembly SIMD support in V8: https://v8.dev/features/simd

On Firefox you need to enable javascript.options.wasm_simd option in about:config.

To use the SIMD version, load argon2-simd.wasm instead of argon2.wasm.

JS Library

The library can be installed from npm:

npm install argon2-browser

Then add this script to your HTML or use your favorite bundler:

<script src="node_modules/argon2-browser/lib/argon2.js"></script>

Alternatively, you can use the bundled version, this way you can include just one script:

<script src="node_modules/argon2-browser/dist/argon2-bundled.js"></script>

Calculate the hash:

argon2.hash({ pass: 'password', salt: 'somesalt' })
    .then(h => console.log(h.hash, h.hashHex, h.encoded))
    .catch(e => console.error(e.message, e.code))

Verify the encoded hash (if you need it):

argon2.verify({ pass: 'password', encoded: 'enc-hash' })
    .then(() => console.log('OK'))
    .catch(e => console.error(e.message, e.code))

Other parameters:

argon2.hash({
    // required
    pass: 'password',
    salt: 'salt',
    // optional
    time: 1, // the number of iterations
    mem: 1024, // used memory, in KiB
    hashLen: 24, // desired hash length
    parallelism: 1, // desired parallelism (it won't be computed in parallel, however)
    secret: new Uint8Array([...]), // optional secret data
    ad: new Uint8Array([...]), // optional associated data
    type: argon2.ArgonType.Argon2d, // Argon2d, Argon2i, Argon2id
})
// result
.then(res => {
    res.hash // hash as Uint8Array
    res.hashHex // hash as hex-string
    res.encoded // encoded hash, as required by argon2
})
// or error
.catch(err => {
    err.message // error message as string, if available
    err.code // numeric error code
})
argon2.verify({
    // required
    pass: 'password',
    encoded: 'enc-hash',
    // optional
    secret: new Uint8Array([...]), // optional secret data
    ad: new Uint8Array([...]), // optional associated data
    type: argon2.ArgonType.Argon2d, // Argon2d, Argon2i, Argon2id. default: guess
})
// result
.then(res => {
    res.hash // hash as Uint8Array
    res.hashHex // hash as hex-string
    res.encoded // encoded hash, as required by argon2
})
// or error
.catch(err => {
    err.message // error message as string, if available
    err.code // numeric error code
})

Usage

You can use this module in several ways:

  1. write the WASM loader manually, for example, if you need more control over memory (example);
  2. bundle it with WebPack or another bundler (example);
  3. in vanilla js: example;
  4. in node.js: example (see a note below).

Bundlers

Node.js support

Of course you can use 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.

Is it used anywhere?

It is! KeeWeb (web-based password manager) is using it as a password hashing function implementation. Check out the source code, if you're interested.

Building

You can build everything with

./build.sh

Prerequisites:

  • emscripten with WebAssembly support (howto)
  • CMake

License

MIT

argon2-browser's People

Contributors

dependabot[bot] avatar dwebfan avatar fellowseb avatar glihm avatar jsbergbau avatar mlaci avatar mymindstorm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

argon2-browser's Issues

Cannot read property 'push' of undefined undefined

hi, I rebuilt this project, but it seems to be a problem.

//environment
ubuntu 16.04
nodejs 10.x.x

//Install emsdk
https://webassembly.org/getting-started/developers-guide/

//rebuild
./build.sh

//run
const argon2=require('./lib/argon2')
argon2.hash({ pass: 'password', salt: 'somesalt' })
.then(h => console.log(h.hash, h.hashHex, h.encoded))
.catch(e => console.error(e.message, e.code))

//result
Cannot read property 'push' of undefined undefined

Verify function could/probably should detect Argon2 type (i, d, id)

Thanks again @antelle for all your hard work! I noticed two issues trying out the verify functionality and the solutions aren't immediately apparent to me or I'd submit a PR.

  1. Argon2 type should be determined from encoded string when available, I think. I'm not sure what's going on in the lines above where the params are being encoded, but it doesn't seem to cover getting the Argon2 type. Without passing the type manually you'll get: Uncaught (in promise) {message: "Decoding failed", code: -32}

Is the best way to correct this to simply search the front of the string for the type identifier? (argon2i, argon2d, argon2id)

2. When I do pass a type into the params for verify I get undefined as the result. The point where things seem to be going awry as best as I can tell is here, where nothing is being returned https://github.com/antelle/argon2-browser/blob/master/lib/argon2.js#L155

Not sure what I did wrong the first time, but this definitely works now.

is there any way to set salt as optional ?

Hello,

As per document password and salt are required field.
But Is there any way to set salt optional or blank ? cause there is also 8 character length is minimum required.

Is the salt included in the hash?

I was wondering as with Bcrypt, is the salt included in the Hash, as I see other implementations like the one in https://github.com/ranisalt/node-argon2 and yours that in some instances don't have a parameter input for the salt in their example:

try {
  if (await argon2.verify("<big long hash>", "password")) {
    // password match
  } else {
    // password did not match
  }
} catch (err) {
  // internal failure
}

If so which part is the salt and will this be compatible when used with another library too?

How to use with a bundler?

Hey there,

thanks for providing this awesome module to the community! I am trying to get this package to work with Webpack & React, but it turns out to be quite the issue. I suspect it is because of the way that the argon2.js:

  1. Uses a factory and does not have a default export (webpack will bundle it in a scope which afaik is unaccessible afterwards), and
  2. Loads the asm-file from a node_modules folder which is supposed to be exposed (if I get this correctly)

I was able to get this to work with a bundler by copying both of these files, exposing the asm variant file, and then both adding a default export & changing the import path. Is there a way to get first hand support for bundlers so that this does not result in any issues? I suspect most people will not be willing to expose their node_modules, and I feel it is a really bad practice. On the other hand, my workaround will need to be updated manually, which might pose security risks at some point in time.

Would love to get your feedback on this!

Cheers

What settings do you recommend please?

Thank you for this useful library porting to JS!

Do you recommend some iteration/memory settings to support the majority of devices please?
For example, there is many people that uses mobile with small capabilities (512MB RAM / 1Ghz CPU for an iPhone 4S).

EDIT: I think the best option is to make some settings tests when the user sign up, and to keep the settings used for this user's device, to be uses when the user sign in. I've explained this here.

Update readme to clarify that asm.js is no longer used

Hi, thanks for writing this library. I noticed that the readme still insists that asm.js is being used in the browser, but I understand it's been removed since 1.5.3, so the readme should probably be updated to reflect that.

emcc -s MODULARIZE=1 generates invalid argon2.js

@antelle ,

first of all thank you for this great repo.

I have cloned and build the repo without any problem with your version. No problem with plain Javascript.
However, I am trying to load the wasm into Angular 10.

To do so, I have modified the build-wasm.sh adding to the -DCMAKE_EXE_LINKER_FLAGS= the emcc option -s MODULARIZE=1 to be able to load it easily from Typescript.
Doing this, the generated argon2.js is no longer valid.

Can you try to add this option and compile to confirm that it's not due to my setup?
Thank you for considering / your help.

Module parse failed: magic header not detected

When I try the example code with simple react-create-app, I got this failure. Seems like wasm file is loaded, but map with wrong loader.

./node_modules/argon2-browser/dist/argon2.wasm
Module parse failed: magic header not detected
File was processed with these loaders:
 * ./node_modules/file-loader/dist/cjs.js
You may need an additional loader to handle the result of these loaders.
Error: magic header not detected

I've tried webpack.config.js, but still same failure. Can you please help with it?

Support for Argon2id

In the source I can see that there is support for d and i versions:

var ArgonType = {
Argon2d: 0,
Argon2i: 1
};

Am I missing something or there is not option to use the hybrid version Argon2id?

Thank you in advance for your great work.

IE11 support

Hi,
Is there any possibility to use this library with Internet Explorer 11?

ReferenceError: TextEncoder is not defined

I have encountered some problems while using it.

//first
npm install argon2-browser

//code
const argon2 = require("argon2-browser");
const _hash = await argon2.hash({
        pass:'password',
        salt: "salt"
    });

//result
ReferenceError: TextEncoder is not defined
ReferenceError: TextEncoder is not defined

E:\work\czr-remix-ide\czr\czr_account.js\node_modules\argon2-browser\dist\argon2.js:1
(function (exports, require, module, __filename, __dirname) { var Module=typeof self!=="undefined"&&typeof self.Module!=="undefined"?self.Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}Module["arguments"]=[];Module["thisProgram"]="./this.program";Module["quit"]=function(status,toThrow){throw toThrow};Module["preRun"]=[];Module["postRun"]=[];var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_HAS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_HAS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_NODE=ENVIRONMENT_HAS_NODE&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONME
abort(ReferenceError: TextEncoder is not defined). Build with -s ASSERTIONS=1 for more info.

Error WebAssembly module is included in initial chunk.

Using the require statement as suggested in the webpack example gives this the error bellow.

Argon2 is only imported in: https://github.com/afrancht/wasm-reproduction/blob/master/components/LandingPage.js

I have tried other types of import found in other GitHub Issues but with no luck!

Error:

[ error ] ./node_modules/argon2-browser/dist/argon2.wasm
WebAssembly module is included in initial chunk.
This is not allowed, because WebAssembly download and compilation must happen asynchronous.
Add an async splitpoint (i. e. import()) somewhere between your entrypoint and the WebAssembly module:
* multi next-client-pages-loader?page=%2F&absolutePagePath=%2FUsers%2Fafrancht%2FDesktop%2Fhello%2Fpages%2Findex.js --> ./node_modules/next/dist/build/webpack/loaders/next-client-pages-loader.js?page=%2F&absolutePagePath=%2FUsers%2Fafrancht%2FDesktop%2Fhello%2Fpages%2Findex.js --> ./pages/index.js --> ./components/index.js --> ./components/LandingPage.js --> ./node_modules/argon2-browser/lib/argon2.js --> ./node_modules/argon2-browser/dist/argon2.wasm

Sorry for bothering again, @antelle :):

Thoughts on use in React Native?

This is beyond the scope of this library, and my question itself is pretty terrible due to not knowing where even to begin with debugging, but I figure that it doesn't hurt to ask if you have any thoughts on how to use argon2-browser in a React Native environment.

Importing and requiring the module in that environment just produce a const with a value of undefined.

Why is the js code I compiled with emscripten different from the contents of the files in your git dist folder?

This is the content of the argon2-asm.min.js file I compiled.

var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}Module["arguments"]=[];Module["thisProgram"]="./this.program";Module["quit"]=function(status,toThrow){throw toThrow};Module["preRun"]=[];Module["postRun"]=[];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 require==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;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)}else{return scriptDirectory+path}}if(ENVIRONMENT_IS_NODE){scriptDirectory=__dirname+"/";var nodeFS;var nodePath;Module["read"]=function shell_read(filename,binary){var ret;if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);ret=nodeFS["readFileSync"](filename);return binary?ret:ret.toString()};Module["readBinary"]=function readBinary(filename){var ret=Module["read"](filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){Module["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}Module["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);Module["quit"]=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){Module["read"]=function shell_read(f){return read(f)}}Module["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"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof quit==="function"){Module["quit"]=function(status){quit(status)}}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}Module["read"]=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){Module["readBinary"]=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}Module["readAsync"]=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)};Module["setWindowTitle"]=function(title){document.title=title}}else{}var out=Module["print"]||(typeof console!=="undefined"?console.log.bind(console):typeof print!=="undefined"?print:null);var err=Module["printErr"]||(typeof printErr!=="undefined"?printErr:typeof console!=="undefined"&&console.warn.bind(console)||out);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=undefined;var asm2wasmImports={"f64-rem":function(x,y){return x%y},"debugger":function(){debugger}};var functionPointers=new Array(0);if(typeof WebAssembly!=="object"){err("no native wasm support detected")}var wasmMemory;var wasmTable;var ABORT=false;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;var WASM_PAGE_SIZE=65536;var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferViews(){Module["HEAP8"]=HEAP8=new Int8Array(buffer);Module["HEAP16"]=HEAP16=new Int16Array(buffer);Module["HEAP32"]=HEAP32=new Int32Array(buffer);Module["HEAPU8"]=HEAPU8=new Uint8Array(buffer);Module["HEAPU16"]=HEAPU16=new Uint16Array(buffer);Module["HEAPU32"]=HEAPU32=new Uint32Array(buffer);Module["HEAPF32"]=HEAPF32=new Float32Array(buffer);Module["HEAPF64"]=HEAPF64=new Float64Array(buffer)}var DYNAMIC_BASE=5248560,DYNAMICTOP_PTR=5424;var TOTAL_STACK=5242880;var INITIAL_TOTAL_MEMORY=Module["TOTAL_MEMORY"]||134217728;if(INITIAL_TOTAL_MEMORY<TOTAL_STACK)err("TOTAL_MEMORY should be larger than TOTAL_STACK, was "+INITIAL_TOTAL_MEMORY+"! (TOTAL_STACK="+TOTAL_STACK+")");if(Module["buffer"]){buffer=Module["buffer"]}else{if(typeof WebAssembly==="object"&&typeof WebAssembly.Memory==="function"){wasmMemory=new WebAssembly.Memory({"initial":INITIAL_TOTAL_MEMORY/WASM_PAGE_SIZE,"maximum":INITIAL_TOTAL_MEMORY/WASM_PAGE_SIZE});buffer=wasmMemory.buffer}else{buffer=new ArrayBuffer(INITIAL_TOTAL_MEMORY)}}updateGlobalBufferViews();HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;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 ensureInitRuntime(){if(runtimeInitialized)return;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"]={};var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return String.prototype.startsWith?filename.startsWith(dataURIPrefix):filename.indexOf(dataURIPrefix)===0}var wasmBinaryFile="argon2-asm.min.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(Module["wasmBinary"]){return new Uint8Array(Module["wasmBinary"])}if(Module["readBinary"]){return Module["readBinary"](wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!Module["wasmBinary"]&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"){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()})}return new Promise(function(resolve,reject){resolve(getBinary())})}function createWasm(env){var info={"env":env,"global":{"NaN":NaN,Infinity:Infinity},"global.Math":Math,"asm2wasm":asm2wasmImports};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}if(!Module["wasmBinary"]&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch==="function"){WebAssembly.instantiateStreaming(fetch(wasmBinaryFile,{credentials:"same-origin"}),info).then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");instantiateArrayBuffer(receiveInstantiatedSource)})}else{instantiateArrayBuffer(receiveInstantiatedSource)}return{}}Module["asm"]=function(global,env,providedBuffer){env["memory"]=wasmMemory;env["table"]=wasmTable=new WebAssembly.Table({"initial":6,"maximum":6,"element":"anyfunc"});env["__memory_base"]=1024;env["__table_base"]=0;var exports=createWasm(env);return exports};function _emscripten_get_heap_size(){return HEAP8.length}function abortOnCannotGrowMemory(requestedSize){abort("OOM")}function _emscripten_resize_heap(requestedSize){abortOnCannotGrowMemory(requestedSize)}function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest)}function _pthread_join(){}function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module["___errno_location"]()>>2]=value;return value}var asmGlobalArg={};var asmLibraryArg={"b":abort,"c":___setErrNo,"h":_emscripten_get_heap_size,"g":_emscripten_memcpy_big,"f":_emscripten_resize_heap,"e":_pthread_join,"d":abortOnCannotGrowMemory,"a":DYNAMICTOP_PTR};var asm=Module["asm"](asmGlobalArg,asmLibraryArg,buffer);Module["asm"]=asm;var _argon2_error_message=Module["_argon2_error_message"]=function(){return Module["asm"]["i"].apply(null,arguments)};var _argon2_hash=Module["_argon2_hash"]=function(){return Module["asm"]["j"].apply(null,arguments)};var _argon2_verify=Module["_argon2_verify"]=function(){return Module["asm"]["k"].apply(null,arguments)};Module["asm"]=asm;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};function run(args){args=args||Module["arguments"];if(runDependencies>0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();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;function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}if(what!==undefined){out(what);err(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;throw"abort("+what+"). Build with -s ASSERTIONS=1 for more info."}Module["abort"]=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}Module["noExitRuntime"]=true;run();

Only this line of code.

It looks completely different from your code.

Running in WebWorker

I see that the demo page runs in a worker thread but this code isn't included in the Node distribution and from what I can see from the demo page, it is quite complicated. I can't get the normal version loading because of

Uncaught (in promise) TypeError: Module scripts are not supported on WorkerGlobalScope yet (see https://crbug.com/680046).
    at loadWasmModule (argon2.js:74)
    at argon2.js:63

Are you planning to include proper worker support & code in the distribution as well? Otherwise, is there any simple way to get it working?

Can't import bundled version in nodejs

$ node
Welcome to Node.js v14.16.0.
Type ".help" for more information.
> require('argon2-browser/dist/argon2-bundled.min')
Uncaught ReferenceError: self is not defined
    at Object.<anonymous> (/.../node_modules/argon2-browser/dist/argon2-bundled.min.js:4:2)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)

argon2-browser version is 1.17.0

At the same time, argon2-browser/lib/argon2.js works fine, but we would prefer to don't import wasm files because our lib has multiple ways to use and this would complicate installation for end users.

Can't reproduce build

I'm having trouble reproducing the build results as found in /dist.

The files I end up with are significantly shorter than the versions that ship with the repo, and moving them into /docs/dist gives me the following output in the browser console when clicking run on test.html:

wasm streaming compile failed: TypeError: NetworkError when attempting to fetch resource. argon2-asm.min.js:1:11123
falling back to ArrayBuffer instantiation argon2-asm.min.js:1:11169
on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS) argon2-asm.min.js:1:15088
on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS) argon2-asm.min.js:1:15098
failed to asynchronously prepare wasm: abort("on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)"). Build with -s ASSERTIONS=1 for more info. argon2-asm.min.js:1:10772
abort("on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)"). Build with -s ASSERTIONS=1 for more info. argon2-asm.min.js:1:15088
abort("on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)"). Build with -s ASSERTIONS=1 for more info. argon2-asm.min.js:1:15098
Module.intArrayFromString is not a function undefined test.html:22:19
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at file:///Users/fang/softsrc/argon2-browser/docs/argon2-asm.min.wasm. (Reason: CORS request not http). 

I don't get any errors when running build.sh, as far as I can tell everything's completing fine. Could there be an emscripten configuration step that I missed? Or perhaps this has to do with me using the latest version of all the required tooling?

In case it's useful, I uploaded pastebins of my outputs for /dist/argon2.js and /dist/argon2-asm.min.js. You can see that especially the latter seems to be missing a lot of information compared to the version in the repo.

If you have any ideas here, they'd be much appreciated. Thank you for your time!

Argon2i hashLen not working.

Hi @antelle !

I'm trying to call your argon2-browser implementation in a Chrome PNaCl environment.

I'm doing this:

listener.addEventListener('load', function(e) {
    moduleEl.postMessage(
        {
            pass: key,
            salt: salt,
            type: 1,//argon2.ArgonType.Argon2i,
            time: 3, // the number of iterations
            mem: 4096, // used memory, in KiB
            hashLen: 64,
            parallelism: 1
        }
    );
}, true);

But when the listener receives the message, the hash length it's always 128. What I'm doing wrong? Is there a bug on the .pexe file?

(If I use AMD it works as expected).

Thanks!

Option to decrypt hash

I couldn't find any option to decrypt a hash in the docs. Is it possible as of today?

As far as I understood, the C implementation (which compiles to JS) already have it, right?

'Encoding failed' error on any `hashLen > 352`

According to spec

Tag length T may be any integer number of bytes from 4 to 2^32-1.
The Argon2 output is a T-length string.

These higher lengths work fine for me with argon2 cli and with the argon2 c reference library.

Reproducible example usable on runkit:

var a2 = require("argon2-browser")
a2.hash({
    pass: 'pass',
    salt: 'somesalt',
    hashLen: 373
}).then(res => console.log(res.hash))
Promise (rejected)
Object {code: -31, message: "Encoding failed"}

Async Await

Instead of promises can we use the async await syntax with argon2-browser?

Significance of config items in webpack example

Hello!

I am implementing this package using webpack. The suggested values for the webpack config, specifically for the node parameter, are:

    // Error: Module not found: Error: Can't resolve 'fs'
    node: {
        __dirname: false,
        fs: 'empty',
        Buffer: false,
        process: false
    }

I was receiving the issue for "Can't resolve 'fs'". This configuration did fix the issue. I am curious why the parameters aside from fs: 'empty' are required/suggested. I removed them and it appears to still fix the issue. The reason this stood out to me is because I use process in some of my webpack config to import variables at build-time. Is this package incompatible with such configurations?

Thank you for taking the time to support this package 👍

Best

Killing Firefox Quantum 65.0.2 (64-bit)

It's probably some Firefox issue, so take it just like a warning.

Tested on three computers, live demo WebAssembly kills Firefox Quantum 65.0.2 (64-bit) Win 10 - hard crash, no error message, just goes down.

Native WASM not working in Firefox 55.0.3

I'm using latest Firefox (version 55.0.3) to test using your demo page (https://antelle.github.io/argon2-browser/) and get the following output, never actually completing.
The asm.js method completes in about 80ms and as far as I know native wasm should just work and be faster as asm.js as well.
Trying things out in Chrome 60 works as expected.

[00.000] Testing Argon2 using Binaryen native-wasm
[00.011] Loading wasm...
[00.027] Wasm loaded, loading script...
[00.054] trying binaryen method: native-wasm
[00.055] asynchronously preparing wasm
[00.056] binaryen method succeeded.
[00.057] Script loaded in 30ms
[00.058] Calculating hash....
[00.090] Params: pass=password, salt=somesalt, time=1, mem=1024, hashLen=32, parallelism=1, type=0
[00.092] Running...

Trouble using verify

Hi,

I've been using the example webpack code, and I'm having some trouble getting verify to work.

Broken code
const argon2 = require('argon2-browser');

argon2
    .hash({
        pass: 'p@ssw0rd',
        salt: 'somesalt'
    })
    .then(hash => {
        console.log(hash);

        argon2
            .verify({
                pass: 'p@ssw0rd',
                encoded: hash.encoded
            })
            .then(() => console.log('Hash correct'))
            .catch(e => console.log(e));
    });

The above code generates a hash correctly, but it never fulfills or rejects the argon2.verify promise.

Working code
argon2
    .verify({
        pass: 'p@ssw0rd',
        encoded:
            '$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$JGOjI98eRw4ahgg28250Fz2BKEFl48Xm'
    })
    .then(() => console.log('Hash correct'))
    .catch(e => console.log(e));

I then manually copied the string into another promise, which works fine and reaches 'Hash correct'.


Additionally, I found that if you try to do more than one thing at once, it will have problems allocating memory.

More bad code
const argon2 = require('argon2-browser');

argon2
    .hash({
        pass: 'p@ssw0rd',
        salt: 'somesalt'
    })
    .then(hash => {
        console.log(hash);
    });

argon2
    .hash({
        pass: 'p@ssw0rd',
        salt: 'somesalt'
    })
    .then(hash => {
        console.log(hash);
    });

This will fail with

argon2.js:77 Uncaught (in promise) TypeError: Module.allocate is not a function
    at allocateArray (argon2.js:77)
    at eval (argon2.js:106)

Not sure if I'm just doing something wrong. I'm hoping that you could point me in the right direction.

Integration with create-react-app

Hi, you made a good job. I've found a problem with create-react-app and i hope you help me)
When i try use argon2-browser with my app, which was initialised from create-react-app. So that create-react-app has a only hide webpack config, i've used react-app-rewired to customise it config. Problems: usage process: false dont allowed in create-react-app, because some packadges depend from it, and also i common use the process to acces env variables. If add your suggested config(without process redefining), i have a error __webpack_public_path__ is not defined. Example of problem create-react-app's project bellow
package.json:

{
  "name": "myapp",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.4.0",
    "@testing-library/user-event": "^7.2.1",
    "argon2-browser": "^1.12.0",
    "react": "^16.12.0",
    "react-app-rewired": "^2.1.5",
    "react-dom": "^16.12.0",
    "react-scripts": "3.3.0"
  },
  "scripts": {
    "start": "react-app-rewired start"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "devDependencies": {
    "base64-loader": "^1.0.0"
  }
}

config-overrides.js:

 module.exports = {
  webpack: function(config, env) {
    config.module = {
      ...config.module,
      noParse: [/\.wasm$/],
      rules: [
        ...config.module.rules,
        {
          test: /\.wasm$/,
          type: 'javascript/auto',
          loaders: ['base64-loader']
        }
      ]
    }
    config.node = {
      ...config.node,
      __dirname: false,
      fs: 'empty',
      Buffer: false,
    };
    return config
  }
}

usage:

import React from 'react';
import ReactDOM from 'react-dom';
const argon2 = require('argon2-browser')

argon2.hash({ pass: 'password', salt: 'somesalt' })
    .then(h => console.log(h.hash, h.hashHex, h.encoded))
    .catch(e => console.error(e.message, e.code))

ReactDOM.render(
Hello
, document.getElementById('root'));

Thanks in advance

How to use WASM version?

Hello!

I'm trying to use the WASM version of argon2-browser in an Angular project.

I've configured the Webpack loader as per https://github.com/antelle/argon2-browser/tree/master/examples/webpack

Everything seems to go fine, except the performance i'm seeing is more in line with the asm.js version and not the WASM version (I'm comparing with the benchmark here: https://caspertech.github.io/crypto-hash-benchmarks.github.io/

Is there anything special I need to do to get it to use the wasm version?

Memory limit

I’ve noticed that the maximum memory has been reduced to 256MB. Above this results in a error (memory allocation error -22).
Previously I could run this up to 1GB. I’m guessing something has been changed in a recent code update.
Is it possible to return to a higher memory potential? Argon2 is frequently used at 512MB or higher as it results in a 1 second calculation time on a native implementation.

Incorrect Hash When Using Unicode

Note: I've also verified that the official CLI does not have an issue itself by comparing results from Go's Argon2id implementation, as well as the rust-argon2 crate for Rust.

First Example

argon2-browser:

[00.010] Params: pass=汉字漢字, salt=asdfasdfasdfasdf, time=1, mem=1024, hashLen=32, parallelism=1, type=2
[00.014] Encoded: $argon2id$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$StqKaEOAhPKzr5njhOV49GAx4PRsZKJYH/qKwYOyz2E
[00.014] Hash: 4ada8a68438084f2b3af99e384e578f46031e0f46c64a2581ffa8ac183b2cf61

Official CLI:

> echo -n "汉字漢字" | argon2 asdfasdfasdfasdf -id -t 1 -k 1024 -p 1 -l 32
Type:           Argon2id
Iterations:     1
Memory:         1024 KiB
Parallelism:    1
Hash:           cf3aa040b123aa2903c08235424d996f2a021b5d83db95bbb574a07a38b0892d
Encoded:        $argon2id$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$zzqgQLEjqikDwII1Qk2ZbyoCG12D25W7tXSgejiwiS0

Second Example

argon2-browser:

[00.011] Params: pass=¢, salt=asdfasdfasdfasdf, time=1, mem=1024, hashLen=32, parallelism=1, type=2
[00.015] Encoded: $argon2id$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$/wXzasAT4YKmNPMsBcJwchOroZ7aHNXgh4NQ4WurUnc
[00.015] Hash: ff05f36ac013e182a634f32c05c2707213aba19eda1cd5e0878350e16bab5277

Official CLI:

> echo -n "¢" | argon2 asdfasdfasdfasdf -id -t 1 -k 1024 -p 1 -l 32
Type:           Argon2id
Iterations:     1
Memory:         1024 KiB
Parallelism:    1
Hash:           dfd4cf692d18cbde3ac98659d29c51af18817870add5b064ec0fcf6cd98c2e4b
Encoded:        $argon2id$v=19$m=1024,t=1,p=1$YXNkZmFzZGZhc2RmYXNkZg$39TPaS0Yy946yYZZ0pxRrxiBeHCt1bBk7A/PbNmMLks

Investigate possibility of re-enabling threads?

Hello!

In issue #15 threads were disabled. My understanding is that this means that generating a hash with a parallelism value of > 1 becomes exponentially more expensive for the defender but cheaper for an attacker using a different library.

WebAssembly Threads are a lot more mature now, being enabled by default in Chrome since v74. Could the possibility of re-enabling threads be investigated?

How to use wasm?

I change loadScript(distPath + '/argon2-asm.min.js'); to loadScript(distPath + '/argon2.min.js');, then I can see argon2.wasm is successfully fetched in the browser, but it shows error Cannot read property 'apply' of undefined when using argon.hash.

Is there an easy way to use wasm?

Cannot resolve 'env' in .../node_modules/argon2-browser/dist'

I am following the example you have for node in my React application. I have added const argon2 = require('argon2-browser'); and I am trying to make use of argon2.hash(...). I encounter the following error for some reason.

[1] [ error ] ./node_modules/argon2-browser/dist/argon2.wasm
[1] Module not found: Can't resolve 'env' in '/path/node_modules/argon2-browser/dist'

I am new to using WASM in node so perhaps it's me not configuring things correctly. I have installed the npm module using npm install --save argon2-browser

Thank you for your time in advance!

Verifying secret

How do I go about using the optional secret data?

The below code works fine for creating the hash, with changing the encodedSecret resulting in a different hash

      const enc = new TextEncoder()
      const encodedSecret = enc.encode(secret)
return argon2.hash({
            pass: inputPassword,
            salt: inputSalt
            time: 1, // the number of iterations
            mem: 128000, // used memory, in KiB
            hashLen: 32, // desired hash length
            parallelism: 1, // desired parallelism (will be computed in parallel only for PNaCl)
            secret: encodedSecret, // optional secret data
            type: argon2.ArgonType.Argon2id, // or argon2.ArgonType.Argon2i or argon2.ArgonType.Argon2id
          })

I can not seem to verify with a secret though and cant find examples.

    const enc = new TextEncoder()
      const encodedSecret = enc.encode(secret)
      return argon2.verify({
        pass: inputPassword,
        encoded: encodedHash,
        secret: encodedSecret,
      })

Seems if I remove the secret from the hash function the verify will work even with the secret argument set which leads me to believe secret is either not implemented or called something other than secret in the verify function.

Could you update the npm version of this package?

The version of 'argon2-browser' in NPM repository is outdated, 1.1.0, which should be updated to 1.2.0.

And I also tried to install via GitHub link directly: npm install --save-dev antelle/argon2-browser#1.2.0, and I found there is no dist or docs directory.

$ tree node_modules/argon2-browser
node_modules/argon2-browser
├── lib
│   └── argon2.js
├── package.json
└── README.md

1 directory, 3 files

Could you move the dist directory out of the docs, to the top level as well?

Support Uint8Array is missing?

Thank you for the library! I'm trying to pass Uint8Array as 'pass' and 'salt' parameters with no luck. Is it currently possible or present on roadmap?

CompileError: WebAssembly.instantiate(): expected magic word 00 61 73 6d

Hi!

Using argon2-browser with webpack brings up the following error message.

Uncaught (in promise) RuntimeError: abort(CompileError: WebAssembly.instantiate(): expected magic word 00 61 73 6d, found 6d 6f 64 75 @+0). Build with -s ASSERTIONS=1 for more info.
    at abort (http://localhost:3000/static/js/0.chunk.js:788:9)
    at http://localhost:3000/static/js/0.chunk.js:870:7

I also get the following warning before:

failed to asynchronously prepare wasm: CompileError: WebAssembly.instantiate(): expected magic word 00 61 73 6d, found 6d 6f 64 75 @+0

image

I would appreciate any hints.

Results of online demo don't match results of official cmd utility

Official cmd utility output:

Type:		Argon2id
Iterations:	30
Memory:		16384 KiB
Parallelism:	1
Hash:		509b6324c651154a958a10e54ade1b8ecdf61702262c9d71a5d5fa3acf66a66fdf82dff4ee3090cca2715cf01474a6f2b203076549048712249730dfd48041a2
Encoded:	$argon2id$v=19$m=16384,t=30,p=1$c29tZXNhbHQ$UJtjJMZRFUqVihDlSt4bjs32FwImLJ1xpdX6Os9mpm/fgt/07jCQzKJxXPAUdKbysgMHZUkEhxIklzDf1IBBog
0.199 seconds
Verification ok

Output on website:

[00.001] Testing Argon2 using Binaryen native-wasm
[00.001] Calculating hash...
[00.011] Params: pass=password, salt=somesalt, time=30, mem=16384, hashLen=64, parallelism=1, type=2
[01.596] Encoded: $argon2id$v=19$m=16384,t=30,p=1$c29tZXNhbHQ$BYZrABo32arXIFUTNZ6kvvjQVro/9ob7JIy2qkb4LOMUxhICAzwl5KoaWk/0fRnqMU3KIjtVvIPaVRo03p1wsA
[01.597] Hash: 05866b001a37d9aad7205513359ea4bef8d056ba3ff686fb248cb6aa46f82ce314c61202033c25e4aa1a5a4ff47d19ea314dca223b55bc83da551a34de9d70b0
[01.597] Elapsed: 1585ms

So the result calculated by official utility doesn't match result on website:

official: UJtjJMZRFUqVihDlSt4bjs32FwImLJ1xpdX6Os9mpm/fgt/07jCQzKJxXPAUdKbysgMHZUkEhxIklzDf1IBBog
website:  BYZrABo32arXIFUTNZ6kvvjQVro/9ob7JIy2qkb4LOMUxhICAzwl5KoaWk/0fRnqMU3KIjtVvIPaVRo03p1wsA

Steps to reproduce

Download and make from https://github.com/P-H-C/phc-winner-argon2/

Run:

$ ./argon2 somesalt -id -t 30 -m 14 -l 64 <<< "password" > outputfile

ReferenceError: process is not defined (WebPack build issue)

Hi it's me again!

I can't seem to get it running with webpack and create-react-app. Maybe you @antelle could have a look? Do you have an idea?

It does build, but when I open the project in the browser (being served by the webpack dev server), it doesn't load the application but throws this error:

Uncaught ReferenceError: process is not defined
    at Object../node_modules/chalk/index.js (index.js:8)
    at __webpack_require__ (bootstrap:784)
    at fn (bootstrap:150)
    at Object../node_modules/react-dev-utils/formatWebpackMessages.js (formatWebpackMessages.js:10)
    at __webpack_require__ (bootstrap:784)
    at fn (bootstrap:150)
    at Object../node_modules/react-dev-utils/webpackHotDevClient.js (webpackHotDevClient.js:22)
    at __webpack_require__ (bootstrap:784)
    at fn (bootstrap:150)
    at Object.1 (serviceWorker.js:141)

Here is my minimal project: https://github.com/tbelch-at-eHealth-Tec/argon2-browser/tree/cra_a2b-webpack

I appreaciate any help!

Best regards,
Tobias

Error: -33: Threading failure

Entering a value of 2 for parallelism causes the following error: Error: -33: Threading failure

This affects every configuration and mode of running EXCEPT PNaCl. I'm seeing that on both mobile and desktop in Chrome.

@antelle @Fang- - Any ideas what might cause this? Is it possible to run Argon2 with parallelism in a browser environment? Particularly, is it possible on mobile?

License file

Hi,
thanks for making it possible to create Argon2 hashes in browsers. I would love to use your project in one of my tools but there is no license file included (just a link to an MIT license template).

Could you please add a LICENSE file so that I know how to credit correctly?

✌️ Pascua

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.