UNPKG

12.2 kBJavaScriptView Raw
1//? if (typeof ISAAC === 'undefined') ISAAC = false;
2/*
3 Copyright (c) 2012 Nevins Bartolomeo <nevins.bartolomeo@gmail.com>
4 Copyright (c) 2012 Shane Girish <shaneGirish@gmail.com>
5 Copyright (c) 2014 Daniel Wirtz <dcode@dcode.io>
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 3. The name of the author may not be used to endorse or promote products
16 derived from this software without specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/**
31//? if (ISAAC)
32 * @license bcrypt-isaac.js (c) 2013 Daniel Wirtz <dcode@dcode.io>
33//? else
34 * @license bcrypt.js (c) 2013 Daniel Wirtz <dcode@dcode.io>
35 * Released under the Apache License, Version 2.0
36 * see: https://github.com/dcodeIO/bcrypt.js for details
37 */
38(function(global) {
39 "use strict";
40
41 /**
42 * bcrypt namespace.
43 * @type {Object.<string,*>}
44 */
45 var bcrypt = {};
46
47 /**
48 * The random implementation to use as a fallback.
49 * @type {?function(number):!Array.<number>}
50 * @inner
51 */
52 var randomFallback = null;
53
54 /**
55 * Generates cryptographically secure random bytes.
56 * @function
57 * @param {number} len Bytes length
58 * @returns {!Array.<number>} Random bytes
59 * @throws {Error} If no random implementation is available
60 * @inner
61 */
62 function random(len) {
63 /* node */ if (typeof module !== 'undefined' && module && module['exports'])
64 try {
65 return require("crypto")['randomBytes'](len);
66 } catch (e) {}
67 /* WCA */ try {
68 var a; (global['crypto']||global['msCrypto'])['getRandomValues'](a = new Uint32Array(len));
69 return Array.prototype.slice.call(a);
70 } catch (e) {}
71 /* fallback */ if (!randomFallback)
72 throw Error("Neither WebCryptoAPI nor a crypto module is available. Use bcrypt.setRandomFallback to set an alternative");
73 return randomFallback(len);
74 }
75
76 // Test if any secure randomness source is available
77 var randomAvailable = false;
78 try {
79 random(1);
80 randomAvailable = true;
81 } catch (e) {}
82
83 // Default fallback, if any
84 randomFallback = /*? if (ISAAC) { */function(len) {
85 for (var a=[], i=0; i<len; ++i)
86 a[i] = ((0.5 + isaac() * 2.3283064365386963e-10) * 256) | 0;
87 return a;
88 };/*? } else { */null;/*? }*/
89
90
91 /**
92 * Sets the pseudo random number generator to use as a fallback if neither node's `crypto` module nor the Web Crypto
93 * API is available. Please note: It is highly important that the PRNG used is cryptographically secure and that it
94 * is seeded properly!
95 * @param {?function(number):!Array.<number>} random Function taking the number of bytes to generate as its
96 * sole argument, returning the corresponding array of cryptographically secure random byte values.
97 * @see http://nodejs.org/api/crypto.html
98 * @see http://www.w3.org/TR/WebCryptoAPI/
99 */
100 bcrypt.setRandomFallback = function(random) {
101 randomFallback = random;
102 };
103
104 /**
105 * Synchronously generates a salt.
106 * @param {number=} rounds Number of rounds to use, defaults to 10 if omitted
107 * @param {number=} seed_length Not supported.
108 * @returns {string} Resulting salt
109 * @throws {Error} If a random fallback is required but not set
110 * @expose
111 */
112 bcrypt.genSaltSync = function(rounds, seed_length) {
113 if (typeof rounds === 'undefined')
114 rounds = GENSALT_DEFAULT_LOG2_ROUNDS;
115 else if (typeof rounds !== 'number')
116 throw Error("Illegal arguments: "+(typeof rounds)+", "+(typeof seed_length));
117 if (rounds < 4 || rounds > 31)
118 throw Error("Illegal number of rounds (4-31): "+rounds);
119 var salt = [];
120 salt.push("$2a$");
121 if (rounds < 10)
122 salt.push("0");
123 salt.push(rounds.toString());
124 salt.push('$');
125 salt.push(base64_encode(random(BCRYPT_SALT_LEN), BCRYPT_SALT_LEN)); // May throw
126 return salt.join('');
127 };
128
129 /**
130 * Asynchronously generates a salt.
131 * @param {(number|function(Error, string=))=} rounds Number of rounds to use, defaults to 10 if omitted
132 * @param {(number|function(Error, string=))=} seed_length Not supported.
133 * @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting salt
134 * @expose
135 */
136 bcrypt.genSalt = function(rounds, seed_length, callback) {
137 if (typeof seed_length === 'function')
138 callback = seed_length,
139 seed_length = undefined; // Not supported.
140 if (typeof rounds === 'function')
141 callback = rounds,
142 rounds = GENSALT_DEFAULT_LOG2_ROUNDS;
143 if (typeof callback !== 'function')
144 throw Error("Illegal callback: "+typeof(callback));
145 if (typeof rounds !== 'number') {
146 nextTick(callback.bind(this, Error("Illegal arguments: "+(typeof rounds))));
147 return;
148 }
149 nextTick(function() { // Pretty thin, but salting is fast enough
150 try {
151 callback(null, bcrypt.genSaltSync(rounds));
152 } catch (err) {
153 callback(err);
154 }
155 });
156 };
157
158 /**
159 * Synchronously generates a hash for the given string.
160 * @param {string} s String to hash
161 * @param {(number|string)=} salt Salt length to generate or salt to use, default to 10
162 * @returns {string} Resulting hash
163 * @expose
164 */
165 bcrypt.hashSync = function(s, salt) {
166 if (typeof salt === 'undefined')
167 salt = GENSALT_DEFAULT_LOG2_ROUNDS;
168 if (typeof salt === 'number')
169 salt = bcrypt.genSaltSync(salt);
170 if (typeof s !== 'string' || typeof salt !== 'string')
171 throw Error("Illegal arguments: "+(typeof s)+', '+(typeof salt));
172 return _hash(s, salt);
173 };
174
175 /**
176 * Asynchronously generates a hash for the given string.
177 * @param {string} s String to hash
178 * @param {number|string} salt Salt length to generate or salt to use
179 * @param {function(Error, string=)} callback Callback receiving the error, if any, and the resulting hash
180 * @param {function(number)=} progressCallback Callback successively called with the percentage of rounds completed
181 * (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms.
182 * @expose
183 */
184 bcrypt.hash = function(s, salt, callback, progressCallback) {
185 if (typeof callback !== 'function')
186 throw Error("Illegal callback: "+typeof(callback));
187 if (typeof s === 'string' && typeof salt === 'number')
188 bcrypt.genSalt(salt, function(err, salt) {
189 _hash(s, salt, callback, progressCallback);
190 });
191 else if (typeof s === 'string' && typeof salt === 'string')
192 _hash(s, salt, callback, progressCallback);
193 else
194 nextTick(callback.bind(this, Error("Illegal arguments: "+(typeof s)+', '+(typeof salt))));
195 };
196
197 /**
198 * Synchronously tests a string against a hash.
199 * @param {string} s String to compare
200 * @param {string} hash Hash to test against
201 * @returns {boolean} true if matching, otherwise false
202 * @throws {Error} If an argument is illegal
203 * @expose
204 */
205 bcrypt.compareSync = function(s, hash) {
206 if (typeof s !== "string" || typeof hash !== "string")
207 throw Error("Illegal arguments: "+(typeof s)+', '+(typeof hash));
208 if (hash.length !== 60)
209 return false;
210 var comp = bcrypt.hashSync(s, hash.substr(0, hash.length-31)),
211 same = comp.length === hash.length,
212 max_length = (comp.length < hash.length) ? comp.length : hash.length;
213 // to prevent timing attacks, should check entire string
214 // don't exit after found to be false
215 for (var i = 0; i < max_length; ++i)
216 if (comp.length >= i && hash.length >= i && comp[i] != hash[i])
217 same = false;
218 return same;
219 };
220
221 /**
222 * Asynchronously compares the given data against the given hash.
223 * @param {string} s Data to compare
224 * @param {string} hash Data to be compared to
225 * @param {function(Error, boolean)} callback Callback receiving the error, if any, otherwise the result
226 * @param {function(number)=} progressCallback Callback successively called with the percentage of rounds completed
227 * (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms.
228 * @throws {Error} If the callback argument is invalid
229 * @expose
230 */
231 bcrypt.compare = function(s, hash, callback, progressCallback) {
232 if (typeof callback !== 'function')
233 throw Error("Illegal callback: "+typeof(callback));
234 if (typeof s !== "string" || typeof hash !== "string") {
235 nextTick(callback.bind(this, Error("Illegal arguments: "+(typeof s)+', '+(typeof hash))));
236 return;
237 }
238 bcrypt.hash(s, hash.substr(0, 29), function(err, comp) {
239 callback(err, hash === comp);
240 }, progressCallback);
241 };
242
243 /**
244 * Gets the number of rounds used to encrypt the specified hash.
245 * @param {string} hash Hash to extract the used number of rounds from
246 * @returns {number} Number of rounds used
247 * @throws {Error} If hash is not a string
248 * @expose
249 */
250 bcrypt.getRounds = function(hash) {
251 if (typeof hash !== "string")
252 throw Error("Illegal arguments: "+(typeof hash));
253 return parseInt(hash.split("$")[2], 10);
254 };
255
256 /**
257 * Gets the salt portion from a hash. Does not validate the hash.
258 * @param {string} hash Hash to extract the salt from
259 * @returns {string} Extracted salt part
260 * @throws {Error} If `hash` is not a string or otherwise invalid
261 * @expose
262 */
263 bcrypt.getSalt = function(hash) {
264 if (typeof hash !== 'string')
265 throw Error("Illegal arguments: "+(typeof hash));
266 if (hash.length !== 60)
267 throw Error("Illegal hash length: "+hash.length+" != 60");
268 return hash.substring(0, 29);
269 };
270
271 //? include("bcrypt/util.js");
272
273 //? include("bcrypt/impl.js");
274
275 //? if (ISAAC) {
276 //? include("bcrypt/prng/accum.js");
277
278 // Start accumulating
279 if (!randomAvailable)
280 accum.start();
281
282 //? include("bcrypt/prng/isaac.js");
283 //? }
284
285 /* CommonJS */ if (typeof module !== 'undefined' && module["exports"])
286 module["exports"] = bcrypt;
287 /* AMD */ else if (typeof define !== 'undefined' && define["amd"])
288 define(function() { return bcrypt; });
289 /* Global */ else
290 (global["dcodeIO"] = global["dcodeIO"] || {})["bcrypt"] = bcrypt;
291
292})(this);