You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
4.2 KiB

var crypto = require('crypto');
const RANDOM_BATCH_SIZE = 256;
var randomIndex;
var randomBytes;
var getNextRandomValue = function() {
if (randomIndex === undefined || randomIndex >= randomBytes.length) {
randomIndex = 0;
randomBytes = crypto.randomBytes(RANDOM_BATCH_SIZE);
}
var result = randomBytes[randomIndex];
randomIndex += 1;
return result;
};
// Generates a random number
var randomNumber = function(max) {
// gives a number between 0 (inclusive) and max (exclusive)
var rand = getNextRandomValue();
while (rand >= 256 - (256 % max)) {
rand = getNextRandomValue();
}
return rand % max;
};
// Possible combinations
var lowercase = 'abcdefghijklmnopqrstuvwxyz',
uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
numbers = '0123456789',
symbols = '!@#$%^&*()+_-=}{[]|:;"/?.><,`~',
similarCharacters = /[ilLI|`oO0]/g,
strictRules = [
{ name: 'lowercase', rule: /[a-z]/ },
{ name: 'uppercase', rule: /[A-Z]/ },
{ name: 'numbers', rule: /[0-9]/ },
{ name: 'symbols', rule: /[!@#$%^&*()+_\-=}{[\]|:;"/?.><,`~]/ }
];
var generate = function(options, pool) {
var password = '',
optionsLength = options.length,
poolLength = pool.length;
for (var i = 0; i < optionsLength; i++) {
password += pool[randomNumber(poolLength)];
}
if (options.strict) {
// Iterate over each rule, checking to see if the password works.
var fitsRules = strictRules.every(function(rule) {
// If the option is not checked, ignore it.
if (options[rule.name] == false) return true;
// Treat symbol differently if explicit string is provided
if (rule.name === 'symbols' && typeof options[rule.name] === 'string') {
// Create a regular expression from the provided symbols
var re = new RegExp('['+options[rule.name]+']');
return re.test(password);
}
// Run the regex on the password and return whether
// or not it matches.
return rule.rule.test(password);
});
// If it doesn't fit the rules, generate a new one (recursion).
if (!fitsRules) return generate(options, pool);
}
return password;
};
// Generate a random password.
module.exports.generate = function(options) {
// Set defaults.
options = options || {};
if (!Object.prototype.hasOwnProperty.call(options, 'length')) options.length = 10;
if (!Object.prototype.hasOwnProperty.call(options, 'numbers')) options.numbers = false;
if (!Object.prototype.hasOwnProperty.call(options, 'symbols')) options.symbols = false;
if (!Object.prototype.hasOwnProperty.call(options, 'exclude')) options.exclude = '';
if (!Object.prototype.hasOwnProperty.call(options, 'uppercase')) options.uppercase = true;
if (!Object.prototype.hasOwnProperty.call(options, 'lowercase')) options.lowercase = true;
if (!Object.prototype.hasOwnProperty.call(options, 'excludeSimilarCharacters')) options.excludeSimilarCharacters = false;
if (!Object.prototype.hasOwnProperty.call(options, 'strict')) options.strict = false;
if (options.strict) {
var minStrictLength = 1 + (options.numbers ? 1 : 0) + (options.symbols ? 1 : 0) + (options.uppercase ? 1 : 0);
if (minStrictLength > options.length) {
throw new TypeError('Length must correlate with strict guidelines');
}
}
// Generate character pool
var pool = '';
// lowercase
if (options.lowercase) {
pool += lowercase;
}
// uppercase
if (options.uppercase) {
pool += uppercase;
}
// numbers
if (options.numbers) {
pool += numbers;
}
// symbols
if (options.symbols) {
if (typeof options.symbols === 'string') {
pool += options.symbols;
} else {
pool += symbols;
}
}
// Throw error if pool is empty.
if (!pool) {
throw new TypeError('At least one rule for pools must be true');
}
// similar characters
if (options.excludeSimilarCharacters) {
pool = pool.replace(similarCharacters, '');
}
// excludes characters from the pool
var i = options.exclude.length;
while (i--) {
pool = pool.replace(options.exclude[i], '');
}
var password = generate(options, pool);
return password;
};
// Generates multiple passwords at once with the same options.
module.exports.generateMultiple = function(amount, options) {
var passwords = [];
for (var i = 0; i < amount; i++) {
passwords[i] = module.exports.generate(options);
}
return passwords;
};