/** * bcrypt namespace. * @type {Object.} */ var bcrypt = {}; /** * The random implementation to use as a fallback. * @type {?function(number):!Array.} * @inner */ var randomFallback = null; /** * Generates cryptographically secure random bytes. * @function * @param {number} len Bytes length * @returns {!Array.} Random bytes * @throws {Error} If no random implementation is available * @inner */ function random(len) { /* node */ if (typeof module !== 'undefined' && module && module['exports']) try { return require("crypto")['randomBytes'](len); } catch (e) {} /* WCA */ try { var a; (self['crypto']||self['msCrypto'])['getRandomValues'](a = new Uint32Array(len)); return Array.prototype.slice.call(a); } catch (e) {} /* fallback */ if (!randomFallback) throw Error("Neither WebCryptoAPI nor a crypto module is available. Use bcrypt.setRandomFallback to set an alternative"); return randomFallback(len); } // Test if any secure randomness source is available var randomAvailable = false; try { random(1); randomAvailable = true; } catch (e) {} // Default fallback, if any randomFallback = /*? if (ISAAC) { */function(len) { for (var a=[], i=0; i} random Function taking the number of bytes to generate as its * sole argument, returning the corresponding array of cryptographically secure random byte values. * @see http://nodejs.org/api/crypto.html * @see http://www.w3.org/TR/WebCryptoAPI/ */ bcrypt.setRandomFallback = function(random) { randomFallback = random; }; /** * Synchronously generates a salt. * @param {number=} rounds Number of rounds to use, defaults to 10 if omitted * @param {number=} seed_length Not supported. * @returns {string} Resulting salt * @throws {Error} If a random fallback is required but not set * @expose */ bcrypt.genSaltSync = function(rounds, seed_length) { rounds = rounds || GENSALT_DEFAULT_LOG2_ROUNDS; if (typeof rounds !== 'number') throw Error("Illegal arguments: "+(typeof rounds)+", "+(typeof seed_length)); if (rounds < 4) rounds = 4; else if (rounds > 31) rounds = 31; var salt = []; salt.push("$2a$"); if (rounds < 10) salt.push("0"); salt.push(rounds.toString()); salt.push('$'); salt.push(base64_encode(random(BCRYPT_SALT_LEN), BCRYPT_SALT_LEN)); // May throw return salt.join(''); }; /** * Asynchronously generates a salt. * @param {(number|function(Error, string=))=} rounds Number of rounds to use, defaults to 10 if omitted * @param {(number|function(Error, string=))=} seed_length Not supported. * @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting salt * @returns {!Promise} If `callback` has been omitted * @throws {Error} If `callback` is present but not a function * @expose */ bcrypt.genSalt = function(rounds, seed_length, callback) { if (typeof seed_length === 'function') callback = seed_length, seed_length = undefined; // Not supported. if (typeof rounds === 'function') callback = rounds, rounds = undefined; if (typeof rounds === 'undefined') rounds = GENSALT_DEFAULT_LOG2_ROUNDS; else if (typeof rounds !== 'number') throw Error("illegal arguments: "+(typeof rounds)); function _async(callback) { nextTick(function() { // Pretty thin, but salting is fast enough try { callback(null, bcrypt.genSaltSync(rounds)); } catch (err) { callback(err); } }); } if (callback) { if (typeof callback !== 'function') throw Error("Illegal callback: "+typeof(callback)); _async(callback); } else return new Promise(function(resolve, reject) { _async(function(err, res) { if (err) { reject(err); return; } resolve(res); }); }); }; /** * Synchronously generates a hash for the given string. * @param {string} s String to hash * @param {(number|string)=} salt Salt length to generate or salt to use, default to 10 * @returns {string} Resulting hash * @expose */ bcrypt.hashSync = function(s, salt) { if (typeof salt === 'undefined') salt = GENSALT_DEFAULT_LOG2_ROUNDS; if (typeof salt === 'number') salt = bcrypt.genSaltSync(salt); if (typeof s !== 'string' || typeof salt !== 'string') throw Error("Illegal arguments: "+(typeof s)+', '+(typeof salt)); return _hash(s, salt); }; /** * Asynchronously generates a hash for the given string. * @param {string} s String to hash * @param {number|string} salt Salt length to generate or salt to use * @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting hash * @param {function(number)=} progressCallback Callback successively called with the percentage of rounds completed * (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms. * @returns {!Promise} If `callback` has been omitted * @throws {Error} If `callback` is present but not a function * @expose */ bcrypt.hash = function(s, salt, callback, progressCallback) { function _async(callback) { if (typeof s === 'string' && typeof salt === 'number') bcrypt.genSalt(salt, function(err, salt) { _hash(s, salt, callback, progressCallback); }); else if (typeof s === 'string' && typeof salt === 'string') _hash(s, salt, callback, progressCallback); else nextTick(callback.bind(this, Error("Illegal arguments: "+(typeof s)+', '+(typeof salt)))); } if (callback) { if (typeof callback !== 'function') throw Error("Illegal callback: "+typeof(callback)); _async(callback); } else return new Promise(function(resolve, reject) { _async(function(err, res) { if (err) { reject(err); return; } resolve(res); }); }); }; /** * Compares two strings of the same length in constant time. * @param {string} known Must be of the correct length * @param {string} unknown Must be the same length as `known` * @returns {boolean} * @inner */ function safeStringCompare(known, unknown) { var right = 0, wrong = 0; for (var i=0, k=known.length; i} b Byte array * @param {number} len Maximum input length * @returns {string} * @expose */ bcrypt.encodeBase64 = base64_encode; /** * Decodes a base64 encoded string to up to len bytes of output, using the custom bcrypt alphabet. * @function * @param {string} s String to decode * @param {number} len Maximum output length * @returns {!Array.} * @expose */ bcrypt.decodeBase64 = base64_decode;