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
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;
|
|
};
|