├── .travis.yml ├── makefile ├── .gitattributes ├── bin.js ├── package.json ├── LICENSE ├── test-async.js ├── .gitignore ├── test-sync.js ├── test ├── async-test.js └── sync-test.js ├── README.md └── bCrypt.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.12" 4 | - "6.10" 5 | - "7.8" -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | MOCHA=node_modules/.bin/mocha 2 | REPORTER=spec 3 | test: 4 | $(MOCHA) $(shell find test -name "*test.js") --reporter $(REPORTER) 5 | async: 6 | $(MOCHA) test/async-test.js --reporter $(REPORTER) 7 | sync: 8 | $(MOCHA) test/sync-test.js --reporter $(REPORTER) 9 | .PHONY: test -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var bcrypt = require("./bCrypt"); 4 | 5 | if (4 !== process.argv.length) { 6 | console.error("Usage: bcrypt-nodejs "); 7 | } 8 | 9 | bcrypt.genSalt(process.argv[2], function (err, salt) { 10 | if (err) { 11 | throw err; 12 | } 13 | 14 | bcrypt.hash(process.argv[3], salt, checkProgress, gotHash); 15 | 16 | function checkProgress(progress) { 17 | process.stdout.write("..."); 18 | } 19 | function gotHash(err, hash) { 20 | if (err) { 21 | throw err; 22 | } 23 | 24 | console.log(); 25 | console.log(hash); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bcrypt-nodejs", 3 | "description": "A native JS bcrypt library for NodeJS.", 4 | "main": "./bCrypt", 5 | "author": "Shane Girish (https://github.com/shaneGirish)", 6 | "version": "0.1.0", 7 | "bin": { 8 | "bcrypt-nodejs": "bin.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/shaneGirish/bcrypt-nodejs.git" 13 | }, 14 | "directories": { 15 | "test": "test" 16 | }, 17 | "scripts": { 18 | "test": "make test" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/shaneGirish/bcrypt-nodejs/issues" 22 | }, 23 | "contributors": [ 24 | "Alex Murray <> (https://github.com/alexmurray)", 25 | "Nicolas Pelletier <> (https://github.com/NicolasPelletier)", 26 | "Josh Rogers <> (https://github.com/geekymole)", 27 | "Noah Isaacson (https://github.com/nisaacson)", 28 | "Joshua Ball (https://github.com/joshball)" 29 | ], 30 | "keywords": [ 31 | "bcrypt", 32 | "javascript", 33 | "js", 34 | "hash", 35 | "password", 36 | "auth", 37 | "authentication", 38 | "encryption", 39 | "crypt", 40 | "crypto" 41 | ], 42 | "devDependencies": { 43 | "mocha": "~1.8.2", 44 | "should": "~1.2.2", 45 | "eyespect": "~0.1.8", 46 | "async": "~0.2.6" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Nevins Bartolomeo 2 | Copyright (c) 2012 Shane Girish 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. The name of the author may not be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /test-async.js: -------------------------------------------------------------------------------- 1 | var bCrypt = require("./bCrypt"); 2 | 3 | var compares = 0; 4 | var salts = []; 5 | var hashes = []; 6 | 7 | console.log("\n\n Salts \n"); 8 | bCrypt.genSalt(8, saltCallback); 9 | bCrypt.genSalt(10, saltCallback); 10 | 11 | function saltCallback(error, result) { 12 | if(!error) { 13 | console.log(result); 14 | } else { 15 | console.log(error); 16 | } 17 | salts.push(result); 18 | if(salts.length == 2) { 19 | console.log("\n\n Hashes \n"); 20 | createHash(salts[0]); 21 | } 22 | } 23 | 24 | function createHash(salt) { 25 | bCrypt.hash("bacon", salt, null, hashCallback); 26 | bCrypt.hash("bacon", salt, null, hashCallback); 27 | } 28 | 29 | function hashCallback(error, result) { 30 | if(!error) { 31 | console.log(result); 32 | } else { 33 | console.log(error); 34 | } 35 | hashes.push(result); 36 | if(hashes.length == 2) { 37 | createHash(salts[1]); 38 | } else if(hashes.length == 4) { 39 | console.log("\n\n True Compares \n"); 40 | compares = 0; 41 | startCompares("bacon", trueCompareCallback); 42 | } 43 | } 44 | 45 | function startCompares(string, callback) { 46 | bCrypt.compare(string, hashes[0], callback); 47 | bCrypt.compare(string, hashes[1], callback); 48 | bCrypt.compare(string, hashes[2], callback); 49 | bCrypt.compare(string, hashes[3], callback); 50 | } 51 | 52 | function trueCompareCallback(error, result) { 53 | if(!error) { 54 | console.log(result); 55 | } else { 56 | console.log(error); 57 | } 58 | if(++compares == 4) { 59 | console.log("\n\n False Compares \n"); 60 | compares = 0; 61 | startCompares("veggies", falseCompareCallback); 62 | } 63 | } 64 | 65 | function falseCompareCallback(error, result) { 66 | if(!error) { 67 | console.log(result); 68 | } else { 69 | console.log(error); 70 | } 71 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | ################# 3 | ## Eclipse 4 | ################# 5 | 6 | *.pydevproject 7 | .project 8 | .metadata 9 | bin/ 10 | tmp/ 11 | *.tmp 12 | *.bak 13 | *.swp 14 | *~.nib 15 | local.properties 16 | .classpath 17 | .settings/ 18 | .loadpath 19 | 20 | # External tool builders 21 | .externalToolBuilders/ 22 | 23 | # Locally stored "Eclipse launch configurations" 24 | *.launch 25 | 26 | # CDT-specific 27 | .cproject 28 | 29 | # PDT-specific 30 | .buildpath 31 | 32 | 33 | ################# 34 | ## Visual Studio 35 | ################# 36 | 37 | ## Ignore Visual Studio temporary files, build results, and 38 | ## files generated by popular Visual Studio add-ons. 39 | 40 | # User-specific files 41 | *.suo 42 | *.user 43 | *.sln.docstates 44 | 45 | # Build results 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | *_i.c 49 | *_p.c 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.vspscc 64 | .builds 65 | *.dotCover 66 | 67 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 68 | #packages/ 69 | 70 | # Visual C++ cache files 71 | ipch/ 72 | *.aps 73 | *.ncb 74 | *.opensdf 75 | *.sdf 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | 81 | # ReSharper is a .NET coding add-in 82 | _ReSharper* 83 | 84 | # Installshield output folder 85 | [Ee]xpress 86 | 87 | # DocProject is a documentation generator add-in 88 | DocProject/buildhelp/ 89 | DocProject/Help/*.HxT 90 | DocProject/Help/*.HxC 91 | DocProject/Help/*.hhc 92 | DocProject/Help/*.hhk 93 | DocProject/Help/*.hhp 94 | DocProject/Help/Html2 95 | DocProject/Help/html 96 | 97 | # Click-Once directory 98 | publish 99 | 100 | # Others 101 | [Bb]in 102 | [Oo]bj 103 | sql 104 | TestResults 105 | *.Cache 106 | ClientBin 107 | stylecop.* 108 | ~$* 109 | *.dbmdl 110 | Generated_Code #added for RIA/Silverlight projects 111 | 112 | # Backup & report files from converting an old project file to a newer 113 | # Visual Studio version. Backup files are not needed, because we have git ;-) 114 | _UpgradeReport_Files/ 115 | Backup*/ 116 | UpgradeLog*.XML 117 | 118 | 119 | 120 | ############ 121 | ## Windows 122 | ############ 123 | 124 | # Windows image file caches 125 | Thumbs.db 126 | 127 | # Folder config file 128 | Desktop.ini 129 | 130 | 131 | ############# 132 | ## Python 133 | ############# 134 | 135 | *.py[co] 136 | 137 | # Packages 138 | *.egg 139 | *.egg-info 140 | dist 141 | build 142 | eggs 143 | parts 144 | bin 145 | var 146 | sdist 147 | develop-eggs 148 | .installed.cfg 149 | 150 | # Installer logs 151 | pip-log.txt 152 | 153 | # Unit test / coverage reports 154 | .coverage 155 | .tox 156 | 157 | #Translations 158 | *.mo 159 | 160 | #Mr Developer 161 | .mr.developer.cfg 162 | 163 | # Mac crap 164 | .DS_Store 165 | -------------------------------------------------------------------------------- /test-sync.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true, indent: 4, stupid: true */ 2 | var bCrypt = require("./bCrypt"); 3 | 4 | console.log("\n\n Salts \n"); 5 | 6 | var salt1 = bCrypt.genSaltSync(8); 7 | console.log(salt1); 8 | 9 | var salt2 = bCrypt.genSaltSync(10); 10 | console.log(salt2); 11 | 12 | 13 | console.log("\n\n Hashes \n"); 14 | 15 | var hash1 = bCrypt.hashSync("super secret", salt1, null); 16 | console.log(hash1); 17 | 18 | var hash2 = bCrypt.hashSync("super secret", salt1, null); 19 | console.log(hash2); 20 | 21 | var hash3 = bCrypt.hashSync("supersecret", salt1, null); 22 | console.log(hash3); 23 | 24 | var hash4 = bCrypt.hashSync("supersecret", salt1, null); 25 | console.log(hash4); 26 | 27 | var hash5 = bCrypt.hashSync("super secret", salt2, null); 28 | console.log(hash5); 29 | 30 | var hash6 = bCrypt.hashSync("super secret", salt2, null); 31 | console.log(hash6); 32 | 33 | var hash7 = bCrypt.hashSync("supersecret", salt2, null); 34 | console.log(hash7); 35 | 36 | var hash8 = bCrypt.hashSync("supersecret", salt2, null); 37 | console.log(hash8); 38 | 39 | var hash9 = bCrypt.hashSync("super secret", null, null); 40 | console.log(hash9); 41 | 42 | var hash0 = bCrypt.hashSync("supersecret", null, null); 43 | console.log(hash0); 44 | 45 | console.log("\n\n First Set of Compares \n"); 46 | 47 | console.log(bCrypt.compareSync("super secret", hash1) ? 'PASSED' : 'FAILED'); 48 | console.log(bCrypt.compareSync("super secret", hash2) ? 'PASSED' : 'FAILED'); 49 | console.log(bCrypt.compareSync("super secret", hash5) ? 'PASSED' : 'FAILED'); 50 | console.log(bCrypt.compareSync("super secret", hash6) ? 'PASSED' : 'FAILED'); 51 | console.log(bCrypt.compareSync("super secret", hash9) ? 'PASSED' : 'FAILED'); 52 | console.log(bCrypt.compareSync("super secret", hash3) ? 'FAILED' : 'PASSED'); 53 | console.log(bCrypt.compareSync("super secret", hash4) ? 'FAILED' : 'PASSED'); 54 | console.log(bCrypt.compareSync("super secret", hash7) ? 'FAILED' : 'PASSED'); 55 | console.log(bCrypt.compareSync("super secret", hash8) ? 'FAILED' : 'PASSED'); 56 | console.log(bCrypt.compareSync("super secret", hash0) ? 'FAILED' : 'PASSED'); 57 | 58 | console.log("\n\n Second Set of Compares \n"); 59 | 60 | console.log(bCrypt.compareSync("supersecret", hash1) ? 'FAILED' : 'PASSED'); 61 | console.log(bCrypt.compareSync("supersecret", hash2) ? 'FAILED' : 'PASSED'); 62 | console.log(bCrypt.compareSync("supersecret", hash5) ? 'FAILED' : 'PASSED'); 63 | console.log(bCrypt.compareSync("supersecret", hash6) ? 'FAILED' : 'PASSED'); 64 | console.log(bCrypt.compareSync("supersecret", hash9) ? 'FAILED' : 'PASSED'); 65 | console.log(bCrypt.compareSync("supersecret", hash3) ? 'PASSED' : 'FAILED'); 66 | console.log(bCrypt.compareSync("supersecret", hash4) ? 'PASSED' : 'FAILED'); 67 | console.log(bCrypt.compareSync("supersecret", hash7) ? 'PASSED' : 'FAILED'); 68 | console.log(bCrypt.compareSync("supersecret", hash8) ? 'PASSED' : 'FAILED'); 69 | console.log(bCrypt.compareSync("supersecret", hash0) ? 'PASSED' : 'FAILED'); 70 | 71 | 72 | console.log('\n\n -------------------- UTF-8 passwords --------------------'); 73 | var pw1 = '\u6e2f', // http://www.fileformat.info/info/unicode/char/6e2f/index.htm 74 | pw2 = '港', // Character 0x6e2f same as pw1. 75 | pw3 = '\u6f2f', // http://www.fileformat.info/info/unicode/char/6f2f/index.htm 76 | pw4 = '漯', // Character 0x6f2f same as pw3. 77 | salt = '$2a$05$0000000000000000000000', 78 | hash_pw1 = bCrypt.hashSync(pw1, salt, null), 79 | hash_pw2 = bCrypt.hashSync(pw2, salt, null), 80 | hash_pw3 = bCrypt.hashSync(pw3, salt, null), 81 | hash_pw4 = bCrypt.hashSync(pw4, salt, null); 82 | 83 | console.log("\n\n Hashes \n"); 84 | console.log(hash_pw1); 85 | console.log(hash_pw2); 86 | console.log(hash_pw3); 87 | console.log(hash_pw4); 88 | 89 | console.log("\n\n Third Set of Compares \n"); 90 | 91 | console.log(bCrypt.compareSync(pw1, hash_pw1) ? 'PASSED' : 'FAILED'); 92 | console.log(bCrypt.compareSync(pw2, hash_pw2) ? 'PASSED' : 'FAILED'); 93 | console.log(bCrypt.compareSync(pw3, hash_pw3) ? 'PASSED' : 'FAILED'); 94 | console.log(bCrypt.compareSync(pw4, hash_pw4) ? 'PASSED' : 'FAILED'); 95 | console.log('Hashes 1 and 3 are different: ' + (hash_pw1 !== hash_pw3 ? 'PASSED' : 'FAILED')); 96 | console.log('Hashes 2 and 4 are different: ' + (hash_pw2 !== hash_pw4 ? 'PASSED' : 'FAILED')); 97 | console.log('Hashes 1 and 2 are the same: ' + (hash_pw1 === hash_pw2 ? 'PASSED' : 'FAILED')); 98 | console.log('Hashes 3 and 4 are the same: ' + (hash_pw3 === hash_pw4 ? 'PASSED' : 'FAILED')); 99 | -------------------------------------------------------------------------------- /test/async-test.js: -------------------------------------------------------------------------------- 1 | var async = require('async') 2 | var inspect = require('eyespect').inspector() 3 | var should = require('should') 4 | var assert = require('assert') 5 | var bCrypt = require("../bCrypt") 6 | 7 | var compares = 0; 8 | var salts = []; 9 | var hashes = []; 10 | 11 | describe('Test Async', function () { 12 | this.timeout('16s') 13 | this.slow('8s') 14 | var bacon = 'bacon' 15 | var veggies = 'veggies' 16 | var soyBaconHash = 'some invalid hash that does not equal sixty bytes in length' 17 | 18 | var salt1, salt2, baconHash1, baconHash2, veggiesHash1, veggiesHash2 19 | before(function (done) { 20 | async.parallel([ 21 | function genSalt1(cb) { 22 | bCrypt.genSalt(8, function (err, reply) { 23 | if (err) { return cb(err) } 24 | should.exist(reply) 25 | salt1 = reply 26 | cb() 27 | }) 28 | }, 29 | function genSalt2(cb) { 30 | bCrypt.genSalt(10, function (err, reply) { 31 | if (err) { return cb(err) } 32 | should.exist(reply) 33 | salt2 = reply 34 | cb() 35 | }) 36 | }, 37 | 38 | function hashBacon1(cb) { 39 | bCrypt.hash(bacon, salt1, null, function (err, reply) { 40 | if (err) { return cb(err) } 41 | should.exist(reply) 42 | baconHash1 = reply 43 | cb() 44 | }) 45 | }, 46 | function hashBacon2(cb) { 47 | bCrypt.hash(bacon, salt2, null, function (err, reply) { 48 | if (err) { return cb(err) } 49 | should.exist(reply) 50 | baconHash2 = reply 51 | cb() 52 | }) 53 | }, 54 | 55 | function hashVeggies1(cb) { 56 | bCrypt.hash(veggies, salt1, null, function (err, reply) { 57 | if (err) { return cb(err) } 58 | should.exist(reply) 59 | veggiesHash1 = reply 60 | cb() 61 | }) 62 | }, 63 | 64 | 65 | function hashVeggies2(cb) { 66 | bCrypt.hash(veggies, salt2, null, function (err, reply) { 67 | if (err) { return cb(err) } 68 | should.exist(reply) 69 | veggiesHash2 = reply 70 | cb() 71 | }) 72 | } 73 | ], function (errs) { 74 | should.not.exist(errs, 'no errors should occur when generating salts') 75 | should.exist(salt1, 'salt1 not set') 76 | should.exist(salt2, 'salt2 not set') 77 | done() 78 | }) 79 | }) 80 | 81 | it('baconHash1 valid compare should match', function (done) { 82 | bCrypt.compare(bacon, baconHash1, function (err, matches) { 83 | should.not.exist(err) 84 | assert.ok(matches, 'compare should return true') 85 | done() 86 | }) 87 | }) 88 | 89 | it('baconHash2 valid compare should match', function (done) { 90 | bCrypt.compare(bacon, baconHash2, function (err, matches) { 91 | should.not.exist(err) 92 | assert.ok(matches, 'compare should return true') 93 | done() 94 | }) 95 | }) 96 | 97 | it('veggiesHash1 valid compare should match', function (done) { 98 | bCrypt.compare(veggies, veggiesHash1, function (err, matches) { 99 | should.not.exist(err) 100 | assert.ok(matches, 'compare should return true') 101 | done() 102 | }) 103 | }) 104 | 105 | it('veggiesHash2 valid compare should match', function (done) { 106 | bCrypt.compare(veggies, veggiesHash2, function (err, matches) { 107 | should.not.exist(err) 108 | assert.ok(matches, 'compare should return true') 109 | done() 110 | }) 111 | }) 112 | 113 | 114 | it('bacon should not match veggiesHash1', function (done) { 115 | bCrypt.compare(bacon, veggiesHash1, function (err, matches) { 116 | should.not.exist(err) 117 | assert.ok(!matches, 'compare should return false') 118 | done() 119 | }) 120 | }) 121 | it('bacon should not match veggiesHash2', function (done) { 122 | bCrypt.compare(bacon, veggiesHash2, function (err, matches) { 123 | should.not.exist(err) 124 | assert.ok(!matches, 'compare should return false') 125 | done() 126 | }) 127 | }) 128 | 129 | 130 | 131 | it('veggies should not match baconHash1', function (done) { 132 | bCrypt.compare(veggies, baconHash1, function (err, matches) { 133 | should.not.exist(err) 134 | assert.ok(!matches, 'compare should return false') 135 | done() 136 | }) 137 | }) 138 | it('veggies should not match baconHash2', function (done) { 139 | bCrypt.compare(veggies, baconHash2, function (err, matches) { 140 | should.not.exist(err) 141 | assert.ok(!matches, 'compare should return false') 142 | done() 143 | }) 144 | }) 145 | it('soyBaconHash is just wrong and should return false and not throw', function(done) { 146 | bCrypt.compare(veggies, soyBaconHash, function (err, matches) { 147 | should.not.exist(err) 148 | assert.ok(!matches, 'compare should return false') 149 | done() 150 | }) 151 | }) 152 | 153 | }) 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Note: This project is not actively maintained 2 | 3 | If you are looking for a javscript-only bcrypt implementation we recommend you use [bcrypt.js](https://github.com/dcodeIO/bcrypt.js), which is based on bcrypt-nodejs. 4 | 5 | 6 | # bcrypt-nodejs 7 | =========================================== 8 | 9 | [![Join the chat at https://gitter.im/shaneGirish/bcrypt-nodejs](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/shaneGirish/bcrypt-nodejs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 10 | [![Build Status](https://secure.travis-ci.org/shaneGirish/bcrypt-nodejs.png)](http://travis-ci.org/shaneGirish/bcrypt-nodejs) 11 | [![Dependency Status](https://david-dm.org/shaneGirish/bcrypt-nodejs.png)](https://david-dm.org/shaneGirish/bcrypt-nodejs) 12 | 13 | 14 | Warning : A change was made in v0.0.3 to allow encoding of UTF-8 encoded strings. This causes strings encoded in v0.0.2 or earlier to not work in v0.0.3 anymore. 15 | 16 | Native JS implementation of BCrypt for Node. 17 | Has the same functionality as [node.bcrypt.js] expect for a few tiny differences. 18 | Mainly, it doesn't let you set the seed length for creating the random byte array. 19 | 20 | I created this version due to a small [problem](https://github.com/ncb000gt/node.bcrypt.js/issues/102) I faced with [node.bcrypt.js]. 21 | Basically, to deploy one of my apps which uses [node.bcrypt.js] on a winx64 platform, I have to force the user to download about 1.6gb of sdks, buildtools and other requirements of which some fail to install ! Microsoft :( 22 | 23 | This code is based on [javascript-bcrypt] and uses [crypto] to create random byte arrays. 24 | 25 | ## Basic usage: 26 | 27 | Installing the Package 28 | 29 | `npm install bcrypt-nodejs` 30 | or 31 | `yarn add bcrypt-nodejs` 32 | 33 | Synchronous 34 | ```javascript 35 | var hash = bcrypt.hashSync("bacon"); 36 | 37 | bcrypt.compareSync("bacon", hash); // true 38 | bcrypt.compareSync("veggies", hash); // false 39 | ``` 40 | 41 | Asynchronous 42 | ```javascript 43 | bcrypt.hash("bacon", null, null, function(err, hash) { 44 | // Store hash in your password DB. 45 | }); 46 | 47 | // Load hash from your password DB. 48 | bcrypt.compare("bacon", hash, function(err, res) { 49 | // res == true 50 | }); 51 | bcrypt.compare("veggies", hash, function(err, res) { 52 | // res = false 53 | }); 54 | ``` 55 | 56 | In the above examples, the salt is automatically generated and attached to the hash. 57 | Though you can use your custom salt and there is no need for salts to be persisted as it will always be included in the final hash result and can be retrieved. 58 | 59 | ## API 60 | * `genSaltSync(rounds)` 61 | * `rounds` - [OPTIONAL] - the number of rounds to process the data for. (default - 10) 62 | * `genSalt(rounds, callback)` 63 | * `rounds` - [OPTIONAL] - the number of rounds to process the data for. (default - 10) 64 | * `callback` - [REQUIRED] - a callback to be fired once the salt has been generated. 65 | * `error` - First parameter to the callback detailing any errors. 66 | * `result` - Second parameter to the callback providing the generated salt. 67 | * `hashSync(data, salt)` 68 | * `data` - [REQUIRED] - the data to be encrypted. 69 | * `salt` - [REQUIRED] - the salt to be used in encryption. 70 | * `hash(data, salt, progress, cb)` 71 | * `data` - [REQUIRED] - the data to be encrypted. 72 | * `salt` - [REQUIRED] - the salt to be used to hash the password. 73 | * `progress` - a callback to be called during the hash calculation to signify progress 74 | * `callback` - [REQUIRED] - a callback to be fired once the data has been encrypted. 75 | * `error` - First parameter to the callback detailing any errors. 76 | * `result` - Second parameter to the callback providing the encrypted form. 77 | * `compareSync(data, encrypted)` 78 | * `data` - [REQUIRED] - data to compare. 79 | * `encrypted` - [REQUIRED] - data to be compared to. 80 | * `compare(data, encrypted, cb)` 81 | * `data` - [REQUIRED] - data to compare. 82 | * `encrypted` - [REQUIRED] - data to be compared to. 83 | * `callback` - [REQUIRED] - a callback to be fired once the data has been compared. 84 | * `error` - First parameter to the callback detailing any errors. 85 | * `result` - Second parameter to the callback providing whether the data and encrypted forms match [true | false]. 86 | * `getRounds(encrypted)` - return the number of rounds used to encrypt a given hash 87 | * `encrypted` - [REQUIRED] - hash from which the number of rounds used should be extracted. 88 | 89 | ## Contributors 90 | * [Alex Murray][alexmurray] 91 | * [Nicolas Pelletier][NicolasPelletier] 92 | * [Josh Rogers][geekymole] 93 | * [Noah Isaacson][nisaacson] 94 | 95 | ## Credits 96 | I heavily reused code from [javascript-bcrypt]. Though "Clipperz Javascript Crypto Library" was removed and its functionality replaced with [crypto]. 97 | 98 | [crypto]:(http://nodejs.org/api/crypto.html) 99 | [node.bcrypt.js]:https://github.com/ncb000gt/node.bcrypt.js.git 100 | [javascript-bcrypt]:https://github.com/nevins-b/javascript-bcrypt 101 | 102 | [alexmurray]:https://github.com/alexmurray 103 | [NicolasPelletier]:https://github.com/NicolasPelletier 104 | [geekymole]:https://github.com/geekymole 105 | [nisaacson]:https://github.com/nisaacson 106 | -------------------------------------------------------------------------------- /test/sync-test.js: -------------------------------------------------------------------------------- 1 | var inspect = require('eyespect').inspector() 2 | var should = require('should') 3 | var assert = require('assert') 4 | var bCrypt = require("../bCrypt") 5 | describe('Test Sync', function () { 6 | this.timeout('100s') 7 | this.slow('50s') 8 | 9 | var salt1, salt2 10 | var secret = "super secret" 11 | var hash1, hash2, hash3, hash4, hash5, hash6, hash7, hash8, hash9, hash0, invalidHash 12 | 13 | var pw1, pw2, pw3, pw4, hash_pw1, hash_pw2, hash_pw3, hash_pw4 14 | before(function () { 15 | inspect('generating hashes, this will take a while') 16 | salt1 = bCrypt.genSaltSync(8) 17 | should.exist(salt1, 'genSaltSync failed') 18 | salt2 = bCrypt.genSaltSync(10) 19 | should.exist(salt2, 'genSaltSync failed') 20 | 21 | hash1 = bCrypt.hashSync('super secret', salt1, null) 22 | hash2 = bCrypt.hashSync('super secret', salt1, null) 23 | hash3 = bCrypt.hashSync('supersecret', salt1, null) 24 | hash4 = bCrypt.hashSync('supersecret', salt1, null) 25 | hash5 = bCrypt.hashSync('super secret', salt2, null) 26 | hash6 = bCrypt.hashSync('super secret', salt2, null) 27 | hash7 = bCrypt.hashSync('supersecret', salt2, null) 28 | hash8 = bCrypt.hashSync('supersecret', salt2, null) 29 | hash9 = bCrypt.hashSync('super secret', null, null) 30 | hash0 = bCrypt.hashSync('super secret', null, null) 31 | 32 | invalidHash = 'some invalid hash that does not equal sixty bytes in length' 33 | 34 | pw1 = '\u6e2f' // http://www.fileformat.info/info/unicode/char/6e2f/index.htm 35 | pw2 = '港' // Character 0x6e2f same as pw1. 36 | pw3 = '\u6f2f' // http://www.fileformat.info/info/unicode/char/6f2f/index.htm 37 | pw4 = '漯' // Character 0x6f2f same as pw3. 38 | 39 | var salt = '$2a$05$0000000000000000000000' 40 | hash_pw1 = bCrypt.hashSync(pw1, salt, null) 41 | hash_pw2 = bCrypt.hashSync(pw2, salt, null) 42 | hash_pw3 = bCrypt.hashSync(pw3, salt, null) 43 | hash_pw4 = bCrypt.hashSync(pw4, salt, null) 44 | }) 45 | 46 | it('should match first set of compares', function () { 47 | 48 | assert.ok(bCrypt.compareSync('super secret', hash1), 'compareSync should return true') 49 | assert.ok(bCrypt.compareSync('super secret', hash2), 'compareSync should return true') 50 | assert.ok(!bCrypt.compareSync('super secret', hash3), 'compareSync should return true') 51 | assert.ok(!bCrypt.compareSync('super secret', hash4), 'compareSync should return true') 52 | assert.ok(bCrypt.compareSync('super secret', hash5), 'compareSync should return true') 53 | assert.ok(bCrypt.compareSync('super secret', hash6), 'compareSync should return true') 54 | assert.ok(!bCrypt.compareSync('super secret', hash7), 'compareSync should return true') 55 | assert.ok(!bCrypt.compareSync('super secret', hash8), 'compareSync should return true') 56 | assert.ok(bCrypt.compareSync('super secret', hash9), 'compareSync should return true') 57 | assert.ok(bCrypt.compareSync('super secret', hash0), 'compareSync should return true') 58 | }) 59 | 60 | it('should match second set of compares', function () { 61 | assert.ok(!bCrypt.compareSync('supersecret', hash1), 'compareSync should return true') 62 | assert.ok(!bCrypt.compareSync('supersecret', hash2), 'compareSync should return true') 63 | assert.ok(bCrypt.compareSync('supersecret', hash3), 'compareSync should return true') 64 | assert.ok(bCrypt.compareSync('supersecret', hash4), 'compareSync should return true') 65 | assert.ok(!bCrypt.compareSync('supersecret', hash5), 'compareSync should return true') 66 | assert.ok(!bCrypt.compareSync('supersecret', hash6), 'compareSync should return true') 67 | assert.ok(bCrypt.compareSync('supersecret', hash7), 'compareSync should return true') 68 | assert.ok(bCrypt.compareSync('supersecret', hash8), 'compareSync should return true') 69 | assert.ok(!bCrypt.compareSync('supersecret', hash9), 'compareSync should return true') 70 | assert.ok(!bCrypt.compareSync('supersecret', hash0), 'compareSync should return true') 71 | }) 72 | 73 | it('hash_pw2 should compare correctly to pw1', function () { 74 | assert.ok(bCrypt.compareSync(pw1, hash_pw1), 'compareSync should return true') 75 | }) 76 | it('hash_pw2 should compare correctly to pw2', function () { 77 | assert.ok(bCrypt.compareSync(pw2, hash_pw2), 'compareSync should return true') 78 | }) 79 | it('hash_pw3 should compare correctly to pw3', function () { 80 | assert.ok(bCrypt.compareSync(pw3, hash_pw3), 'compareSync should return true') 81 | }) 82 | it('hash_pw4 should compare correctly to pw4', function () { 83 | assert.ok(bCrypt.compareSync(pw4, hash_pw4), 'compareSync should return true') 84 | }) 85 | 86 | 87 | it('hash_pw1 should be different than hash_pw3', function () { 88 | hash_pw1.should.not.eql(hash_pw3, 'hash_pw1 should be different from hash_pw3') 89 | }) 90 | 91 | it('hash_pw2 should be different than hash_pw4', function () { 92 | hash_pw2.should.not.eql(hash_pw4, 'hash_pw2 should be different from hash_pw4') 93 | }) 94 | it('hash_pw1 should be equal hash_pw2', function () { 95 | hash_pw1.should.eql(hash_pw2, 'hash_pw1 should equal hash_pw2') 96 | }) 97 | it('hash_pw3 should be equal hash_pw4', function () { 98 | hash_pw3.should.eql(hash_pw3, 'hash_pw3 should equal hash_pw4') 99 | }) 100 | it('invalid hash should return false and not throw', function() { 101 | bCrypt.compareSync('supersecret', invalidHash).should.be.false 102 | }) 103 | }) 104 | -------------------------------------------------------------------------------- /bCrypt.js: -------------------------------------------------------------------------------- 1 | var crypto = require("crypto"); 2 | 3 | var BCRYPT_SALT_LEN = 16; 4 | 5 | var GENSALT_DEFAULT_LOG2_ROUNDS = 10; 6 | var BLOWFISH_NUM_ROUNDS = 16; 7 | 8 | var MAX_EXECUTION_TIME = 100; 9 | var P_orig = [0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 10 | 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 11 | 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 12 | 0xb5470917, 0x9216d5d9, 0x8979fb1b]; 13 | var S_orig = [0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 14 | 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 15 | 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 16 | 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 17 | 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 18 | 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 19 | 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 20 | 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 21 | 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 22 | 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 23 | 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 24 | 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 25 | 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 26 | 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 27 | 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 28 | 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 29 | 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 30 | 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 31 | 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 32 | 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 33 | 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 34 | 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 35 | 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 36 | 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 37 | 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 38 | 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 39 | 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 40 | 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 41 | 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 42 | 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 43 | 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 44 | 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 45 | 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 46 | 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 47 | 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 48 | 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 49 | 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 50 | 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 51 | 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 52 | 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 53 | 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 54 | 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 55 | 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 56 | 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 57 | 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 58 | 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 59 | 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 60 | 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 61 | 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 62 | 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 63 | 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 64 | 0x6e85076a, 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 65 | 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 66 | 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 67 | 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 68 | 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 69 | 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 70 | 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 71 | 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 72 | 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 73 | 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 74 | 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 75 | 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 76 | 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 77 | 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 78 | 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810, 79 | 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 80 | 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 81 | 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 82 | 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 83 | 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, 84 | 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 85 | 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 86 | 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 87 | 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 88 | 0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 89 | 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708, 90 | 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 91 | 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 92 | 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 93 | 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 94 | 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 95 | 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 96 | 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 97 | 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 98 | 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 99 | 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 100 | 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 101 | 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 102 | 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 103 | 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 104 | 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 105 | 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 106 | 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 107 | 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, 0x1462b174, 108 | 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 109 | 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 110 | 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 111 | 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 112 | 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 113 | 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 114 | 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 115 | 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 116 | 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 117 | 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 118 | 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 119 | 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 120 | 0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 121 | 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 122 | 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 123 | 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 124 | 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 125 | 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 126 | 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 127 | 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 128 | 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 129 | 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 130 | 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 131 | 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 132 | 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 133 | 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 134 | 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, 135 | 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 136 | 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 137 | 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 138 | 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 139 | 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 140 | 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 141 | 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 142 | 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 143 | 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 144 | 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 145 | 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 146 | 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 147 | 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 148 | 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 149 | 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 150 | 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 151 | 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 152 | 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 153 | 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 154 | 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 155 | 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 156 | 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 157 | 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 158 | 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 159 | 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 160 | 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 161 | 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 162 | 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 163 | 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3, 164 | 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 165 | 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 166 | 0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf, 167 | 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 168 | 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 169 | 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 170 | 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 171 | 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 172 | 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 173 | 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 174 | 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 175 | 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 176 | 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 177 | 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 178 | 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 179 | 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 180 | 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 181 | 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 182 | 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 183 | 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 184 | 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 185 | 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 186 | 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 187 | 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 188 | 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 189 | 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 190 | 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 191 | 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 192 | 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 193 | 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 194 | 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 195 | 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 196 | 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 197 | 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 198 | 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 199 | 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 200 | 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 201 | 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361, 202 | 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 203 | 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 204 | 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 205 | 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 206 | 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 207 | 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 208 | 0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 209 | 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, 210 | 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 211 | 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 212 | 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 213 | 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 214 | 0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 215 | 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 216 | 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 217 | 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6]; 218 | var bf_crypt_ciphertext = [0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 219 | 0x63727944, 0x6f756274]; 220 | var base64_code = ['.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 221 | 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 222 | 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 223 | 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 224 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', 225 | '9']; 226 | var index_64 = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 227 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 228 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 229 | 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 230 | 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 231 | 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 232 | 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 233 | 49, 50, 51, 52, 53, -1, -1, -1, -1, -1]; 234 | 235 | function getByte(c) { 236 | var ret = 0; 237 | try { 238 | var b = c.charCodeAt(0); 239 | } catch (err) { 240 | b = c; 241 | } 242 | if (b > 127) { 243 | return -128 + (b % 128); 244 | } else { 245 | return b; 246 | } 247 | }; 248 | 249 | function encode_base64(d, len) { 250 | var off = 0; 251 | var rs = []; 252 | var c1; 253 | var c2; 254 | if (len <= 0 || len > d.length) 255 | throw "Invalid len"; 256 | while (off < len) { 257 | c1 = d[off++] & 0xff; 258 | rs.push(base64_code[(c1 >> 2) & 0x3f]); 259 | c1 = (c1 & 0x03) << 4; 260 | if (off >= len) { 261 | rs.push(base64_code[c1 & 0x3f]); 262 | break; 263 | } 264 | c2 = d[off++] & 0xff; 265 | c1 |= (c2 >> 4) & 0x0f; 266 | rs.push(base64_code[c1 & 0x3f]); 267 | c1 = (c2 & 0x0f) << 2; 268 | if (off >= len) { 269 | rs.push(base64_code[c1 & 0x3f]); 270 | break; 271 | } 272 | c2 = d[off++] & 0xff; 273 | c1 |= (c2 >> 6) & 0x03; 274 | rs.push(base64_code[c1 & 0x3f]); 275 | rs.push(base64_code[c2 & 0x3f]); 276 | } 277 | return rs.join(''); 278 | }; 279 | 280 | function char64(x) { 281 | var code = x.charCodeAt(0); 282 | if (code < 0 || code > index_64.length) { 283 | return -1; 284 | } 285 | return index_64[code]; 286 | }; 287 | 288 | function decode_base64(s, maxolen) { 289 | var off = 0; 290 | var slen = s.length; 291 | var olen = 0; 292 | var rs = []; 293 | var c1, c2, c3, c4, o; 294 | if (maxolen <= 0) throw "Invalid maxolen"; 295 | while (off < slen - 1 && olen < maxolen) { 296 | c1 = char64(s.charAt(off++)); 297 | c2 = char64(s.charAt(off++)); 298 | if (c1 == -1 || c2 == -1) { 299 | break; 300 | } 301 | o = getByte(c1 << 2); 302 | o |= (c2 & 0x30) >> 4; 303 | rs.push(String.fromCharCode(o)); 304 | if (++olen >= maxolen || off >= slen) { 305 | break; 306 | } 307 | c3 = char64(s.charAt(off++)); 308 | if (c3 == -1) { 309 | break; 310 | } 311 | o = getByte((c2 & 0x0f) << 4); 312 | o |= (c3 & 0x3c) >> 2; 313 | rs.push(String.fromCharCode(o)); 314 | if (++olen >= maxolen || off >= slen) { 315 | break; 316 | } 317 | c4 = char64(s.charAt(off++)); 318 | o = getByte((c3 & 0x03) << 6); 319 | o |= c4; 320 | rs.push(String.fromCharCode(o)); 321 | ++olen; 322 | } 323 | var ret = []; 324 | for (off = 0; off < olen; off++) { 325 | ret.push(getByte(rs[off])); 326 | } 327 | return ret; 328 | }; 329 | 330 | function encipher(lr, off, P, S) { 331 | var i; 332 | var n; 333 | var l = lr[off]; 334 | var r = lr[off + 1]; 335 | 336 | l ^= P[0]; 337 | for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) { 338 | // Feistel substitution on left word 339 | n = S[(l >> 24) & 0xff]; 340 | n += S[0x100 | ((l >> 16) & 0xff)]; 341 | n ^= S[0x200 | ((l >> 8) & 0xff)]; 342 | n += S[0x300 | (l & 0xff)]; 343 | r ^= n ^ P[++i]; 344 | 345 | // Feistel substitution on right word 346 | n = S[(r >> 24) & 0xff]; 347 | n += S[0x100 | ((r >> 16) & 0xff)]; 348 | n ^= S[0x200 | ((r >> 8) & 0xff)]; 349 | n += S[0x300 | (r & 0xff)]; 350 | l ^= n ^ P[++i]; 351 | } 352 | lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; 353 | lr[off + 1] = l; 354 | return lr; 355 | }; 356 | 357 | function streamtoword(data, offp) { 358 | var i; 359 | var word = 0; 360 | for (i = 0; i < 4; i++) { 361 | word = (word << 8) | (data[offp] & 0xff); 362 | offp = (offp + 1) % data.length; 363 | } 364 | return {key:word, offp:offp}; 365 | }; 366 | 367 | function key(key, P, S) { 368 | var i; 369 | var offp = 0; 370 | var lr = new Array(0x00000000, 0x00000000); 371 | var plen = P.length; 372 | var slen = S.length; 373 | 374 | for (i = 0; i < plen; i++) { 375 | var sw = streamtoword(key, offp); 376 | offp = sw.offp; 377 | P[i] = P[i] ^ sw.key; 378 | } 379 | for (i = 0; i < plen; i += 2) { 380 | lr = encipher(lr, 0, P, S); 381 | P[i] = lr[0]; 382 | P[i + 1] = lr[1]; 383 | } 384 | 385 | for (i = 0; i < slen; i += 2) { 386 | lr = encipher(lr, 0, P, S); 387 | S[i] = lr[0]; 388 | S[i + 1] = lr[1]; 389 | } 390 | }; 391 | 392 | function ekskey(data, key, P, S) { 393 | var i; 394 | var offp = 0; 395 | var lr = new Array(0x00000000, 0x00000000); 396 | var plen = P.length; 397 | var slen = S.length; 398 | var sw; 399 | 400 | for (i = 0; i < plen; i++) { 401 | sw = streamtoword(key, offp); 402 | offp = sw.offp; 403 | P[i] = P[i] ^ sw.key; 404 | } 405 | offp = 0; 406 | for (i = 0; i < plen; i += 2) { 407 | sw = streamtoword(data, offp); 408 | offp = sw.offp; 409 | lr[0] ^= sw.key; 410 | 411 | sw = streamtoword(data, offp); 412 | offp = sw.offp; 413 | lr[1] ^= sw.key; 414 | 415 | lr = encipher(lr, 0, P, S); 416 | P[i] = lr[0]; 417 | P[i + 1] = lr[1]; 418 | } 419 | for (i = 0; i < slen; i += 2) { 420 | sw = streamtoword(data, offp); 421 | offp = sw.offp; 422 | lr[0] ^= sw.key; 423 | 424 | sw = streamtoword(data, offp); 425 | offp = sw.offp; 426 | lr[1] ^= sw.key; 427 | 428 | lr = encipher(lr, 0, P, S); 429 | S[i] = lr[0]; 430 | S[i + 1] = lr[1]; 431 | } 432 | }; 433 | 434 | function crypt_raw(password, salt, log_rounds, progress) { 435 | var rounds; 436 | var j; 437 | var cdata = bf_crypt_ciphertext.slice(); 438 | var clen = cdata.length; 439 | var one_percent; 440 | 441 | if (log_rounds < 4 || log_rounds > 31) 442 | throw "Bad number of rounds"; 443 | if (salt.length != BCRYPT_SALT_LEN) 444 | throw "Bad salt length"; 445 | 446 | rounds = 1 << log_rounds; 447 | one_percent = Math.floor(rounds / 100) + 1; 448 | 449 | var P = P_orig.slice(); 450 | var S = S_orig.slice(); 451 | 452 | ekskey(salt, password, P, S); 453 | 454 | var i = 0; 455 | 456 | while(true) { 457 | if(i < rounds){ 458 | var start = new Date(); 459 | for (; i < rounds;) { 460 | i = i + 1; 461 | key(password, P, S); 462 | key(salt, P, S); 463 | if(i % one_percent == 0){ 464 | progress(); 465 | } 466 | if((new Date() - start) > MAX_EXECUTION_TIME){ 467 | break; 468 | } 469 | } 470 | } else { 471 | for (i = 0; i < 64; i++) { 472 | for (j = 0; j < (clen >> 1); j++) { 473 | var lr = encipher(cdata, j << 1, P, S); 474 | } 475 | } 476 | var ret = []; 477 | for (i = 0; i < clen; i++) { 478 | ret.push(getByte((cdata[i] >> 24) & 0xff)); 479 | ret.push(getByte((cdata[i] >> 16) & 0xff)); 480 | ret.push(getByte((cdata[i] >> 8) & 0xff)); 481 | ret.push(getByte(cdata[i] & 0xff)); 482 | } 483 | return(ret); 484 | } 485 | } 486 | }; 487 | 488 | function hashpw(password, salt, progress) { 489 | var real_salt; 490 | var passwordb = []; 491 | var saltb = []; 492 | var hashed = []; 493 | var minor = String.fromCharCode(0); 494 | var rounds = 0; 495 | var off = 0; 496 | 497 | if (!progress){ 498 | progress = function() {}; 499 | } 500 | 501 | if (salt.charAt(0) != '$' || salt.charAt(1) != '2') 502 | throw "Invalid salt version"; 503 | if (salt.charAt(2) == '$') 504 | off = 3; 505 | else { 506 | minor = salt.charAt(2); 507 | if (minor != 'a' || salt.charAt(3) != '$') 508 | throw "Invalid salt revision"; 509 | off = 4; 510 | } 511 | 512 | // Extract number of rounds 513 | if (salt.charAt(off + 2) > '$') 514 | throw "Missing salt rounds"; 515 | var r1 = parseInt(salt.substring(off, off + 1)) * 10; 516 | var r2 = parseInt(salt.substring(off + 1, off + 2)); 517 | rounds = r1 + r2; 518 | real_salt = salt.substring(off + 3, off + 25); 519 | password = password + (minor >= 'a' ? "\x00" : ""); 520 | 521 | var buf = new Buffer(password); 522 | for (var r = 0; r < buf.length; r++) { 523 | passwordb.push(buf[r]); 524 | } 525 | saltb = decode_base64(real_salt, BCRYPT_SALT_LEN); 526 | hashed = crypt_raw(passwordb, saltb, rounds, progress); 527 | 528 | var rs = []; 529 | rs.push("$2"); 530 | if (minor >= 'a') 531 | rs.push(minor); 532 | rs.push("$"); 533 | if (rounds < 10) 534 | rs.push("0"); 535 | rs.push(rounds.toString()); 536 | rs.push("$"); 537 | rs.push(encode_base64(saltb, saltb.length)); 538 | rs.push(encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1)); 539 | 540 | return(rs.join('')); 541 | }; 542 | 543 | function gensalt(rounds) { 544 | var iteration_count = rounds; 545 | if (iteration_count < 4 || iteration_count > 31) { 546 | iteration_count = GENSALT_DEFAULT_LOG2_ROUNDS; 547 | } 548 | var output = []; 549 | output.push("$2a$"); 550 | if (iteration_count < 10) 551 | output.push("0"); 552 | output.push(iteration_count.toString()); 553 | output.push('$'); 554 | 555 | var rand_buf; 556 | try { 557 | rand_buf = crypto.randomBytes(BCRYPT_SALT_LEN); 558 | } catch (ex) { 559 | throw ex; 560 | } 561 | 562 | output.push(encode_base64(rand_buf, BCRYPT_SALT_LEN)); 563 | return output.join(''); 564 | }; 565 | 566 | function genSaltSync(rounds) { 567 | /* 568 | rounds - [OPTIONAL] - the number of rounds to process the data for. (default - 10) 569 | seed_length - [OPTIONAL] - RAND_bytes wants a length. to make that a bit flexible, you can specify a seed_length. (default - 20) 570 | */ 571 | if(!rounds) { 572 | rounds = GENSALT_DEFAULT_LOG2_ROUNDS; 573 | } 574 | return gensalt(rounds); 575 | } 576 | 577 | function genSalt(rounds, callback) { 578 | /* 579 | rounds - [OPTIONAL] - the number of rounds to process the data for. (default - 10) 580 | seed_length - [OPTIONAL] - RAND_bytes wants a length. to make that a bit flexible, you can specify a seed_length. (default - 20) 581 | callback - [REQUIRED] - a callback to be fired once the salt has been generated. uses eio making it asynchronous. 582 | error - First parameter to the callback detailing any errors. 583 | salt - Second parameter to the callback providing the generated salt. 584 | */ 585 | if(!callback) { 586 | throw "No callback function was given." 587 | } 588 | process.nextTick(function() { 589 | var result = null; 590 | var error = null; 591 | try { 592 | result = genSaltSync(rounds) 593 | } catch(err) { 594 | error = err; 595 | } 596 | callback(error, result); 597 | }); 598 | } 599 | 600 | function hashSync(data, salt, progress) { 601 | /* 602 | data - [REQUIRED] - the data to be encrypted. 603 | salt - [REQUIRED] - the salt to be used in encryption. 604 | */ 605 | if(!salt) { 606 | salt = genSaltSync(); 607 | } 608 | return hashpw(data, salt, progress); 609 | } 610 | 611 | function hash(data, salt, progress, callback) { 612 | /* 613 | data - [REQUIRED] - the data to be encrypted. 614 | salt - [REQUIRED] - the salt to be used to hash the password. if specified as a number then a salt will be generated and used (see examples). 615 | progress - a callback to be called during the hash calculation to signify progress 616 | callback - [REQUIRED] - a callback to be fired once the data has been encrypted. uses eio making it asynchronous. 617 | error - First parameter to the callback detailing any errors. 618 | encrypted - Second parameter to the callback providing the encrypted form. 619 | */ 620 | if (!progress && !callback) { 621 | throw "No callback function was given." 622 | } else if (progress && !callback) { 623 | callback = progress; 624 | progress = undefined; 625 | } 626 | process.nextTick(function() { 627 | var result = null; 628 | var error = null; 629 | try { 630 | result = hashSync(data, salt, progress) 631 | } catch(err) { 632 | error = err; 633 | } 634 | callback(error, result); 635 | }); 636 | } 637 | 638 | function compareSync(data, encrypted) { 639 | /* 640 | data - [REQUIRED] - data to compare. 641 | encrypted - [REQUIRED] - data to be compared to. 642 | */ 643 | 644 | if(typeof data != "string" || typeof encrypted != "string") { 645 | throw "Incorrect arguments"; 646 | } 647 | 648 | var encrypted_length = encrypted.length; 649 | 650 | if(encrypted_length != 60) { 651 | return false; 652 | } 653 | 654 | var same = true; 655 | var hash_data = hashSync(data, encrypted.substr(0, encrypted_length-31)); 656 | var hash_data_length = hash_data.length; 657 | 658 | same = hash_data_length == encrypted_length; 659 | 660 | var max_length = (hash_data_length < encrypted_length) ? hash_data_length : encrypted_length; 661 | 662 | // to prevent timing attacks, should check entire string 663 | // don't exit after found to be false 664 | for (var i = 0; i < max_length; ++i) { 665 | if (hash_data_length >= i && encrypted_length >= i && hash_data[i] != encrypted[i]) { 666 | same = false; 667 | } 668 | } 669 | 670 | return same; 671 | } 672 | 673 | function compare(data, encrypted, callback) { 674 | /* 675 | data - [REQUIRED] - data to compare. 676 | encrypted - [REQUIRED] - data to be compared to. 677 | callback - [REQUIRED] - a callback to be fired once the data has been compared. uses eio making it asynchronous. 678 | error - First parameter to the callback detailing any errors. 679 | same - Second parameter to the callback providing whether the data and encrypted forms match [true | false]. 680 | */ 681 | if(!callback) { 682 | throw "No callback function was given." 683 | } 684 | process.nextTick(function() { 685 | var result = null; 686 | var error = null; 687 | try { 688 | result = compareSync(data, encrypted) 689 | } catch(err) { 690 | error = err; 691 | } 692 | callback(error, result); 693 | }); 694 | } 695 | 696 | function getRounds(encrypted) { 697 | //encrypted - [REQUIRED] - hash from which the number of rounds used should be extracted. 698 | if(typeof encrypted != "string") { 699 | throw "Incorrect arguments"; 700 | } 701 | return Number(encrypted.split("$")[2]); 702 | } 703 | 704 | exports.genSaltSync = genSaltSync; 705 | exports.genSalt = genSalt; 706 | exports.hashSync = hashSync; 707 | exports.hash = hash; 708 | exports.compareSync = compareSync; 709 | exports.compare = compare; 710 | exports.getRounds = getRounds; 711 | --------------------------------------------------------------------------------