Created
March 10, 2022 07:59
-
-
Save intrnl/00d49a0c9e7306d9f17774bd428cc358 to your computer and use it in GitHub Desktop.
Dashlane's random password generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @fileoverview | |
* Dashlane's password generator | |
* https://www.dashlane.com/features/password-generator | |
*/ | |
/** | |
* Generates a password | |
* @param {PasswordGeneratorOptions} options | |
* @returns {string} | |
*/ | |
function generate (options) { | |
const { length, digits, letters, symbols, avoidAmbiguous } = options; | |
if (length < 4) { | |
throw new Error('The length of password cannot be below 4 chars'); | |
} | |
if (!digits && !letters && !symbols) { | |
throw new Error('You need to have atleast one kind of char selected'); | |
} | |
let result = generateWithMandatoryChars({ | |
digits, | |
letters, | |
symbols, | |
avoidAmbiguous, | |
}); | |
const charset = generateAllPossibleChars({ | |
digits, | |
letters, | |
symbols, | |
avoidAmbiguous, | |
}); | |
for (; result.length < length;) { | |
result += getRandomCharFrom(charset); | |
} | |
return shuffle(result); | |
} | |
/** | |
* @typedef {object} PasswordGeneratorOptions | |
* @property {boolean} digits Include numerical digits | |
* @property {boolean} letters Include alphabetical letters | |
* @property {boolean} symbols Include symbols | |
* @property {boolean} avoidAmbiguous Avoid ambiguous characters | |
*/ | |
/** | |
* @param {boolean} avoidAmbiguous | |
* @returns {string} | |
*/ | |
function getNumerals (avoidAmbiguous) { | |
return avoidAmbiguous ? '3456789' : '1234567890'; | |
} | |
/** | |
* @param {boolean} avoidAmbiguous | |
* @returns {string} | |
*/ | |
function getAlphaLower (avoidAmbiguous) { | |
return avoidAmbiguous ? 'abcdefghijkmnopqrstxyz' : 'abcdefghijklmnopqrstuvwxyz'; | |
} | |
/** | |
* @param {boolean} avoidAmbiguous | |
* @returns {string} | |
*/ | |
function getAlphaUpper (avoidAmbiguous) { | |
return avoidAmbiguous ? 'ABCDEFGHJKLMNPQRSTXY' : 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; | |
} | |
/** | |
* @returns {string} | |
*/ | |
function getSymbols () { | |
return '&@$!#?'; | |
} | |
/** | |
* @param {GeneratorOptions} options | |
* @returns {string} | |
*/ | |
function generateWithMandatoryChars (options) { | |
const { avoidAmbiguous } = options; | |
let result = ''; | |
if (options.digits) { | |
result += getRandomCharFrom(getNumerals(avoidAmbiguous)); | |
} | |
if (options.letters) { | |
result += getRandomCharFrom(getAlphaLower(avoidAmbiguous)); | |
result += getRandomCharFrom(getAlphaUpper(avoidAmbiguous)); | |
} | |
if (options.symbols) { | |
result += getRandomCharFrom(getSymbols()); | |
} | |
return result; | |
} | |
/** | |
* @param {GeneratorOptions} options | |
* @returns {string} | |
*/ | |
function getAllPossibleChars (options) { | |
const { avoidAmbiguous } = options; | |
let charset = ''; | |
if (options.digits) { | |
charset += getNumerals(avoidAmbiguous); | |
} | |
if (options.letters) { | |
charset += getAlphaLower(avoidAmbiguous); | |
charset += getAlphaUpper(avoidAmbiguous); | |
} | |
if (options.symbols) { | |
charset += getSymbols(); | |
} | |
return charset; | |
} | |
/** | |
* @typedef {object} GeneratorOptions | |
* @property {boolean} digits Include numerical digits | |
* @property {boolean} letters Include alphabetical letters | |
* @property {boolean} symbols Include symbols | |
* @property {boolean} avoidAmbiguous Avoid ambiguous characters | |
*/ | |
/** | |
* @param {string} str | |
* @returns {string} | |
*/ | |
function shuffle (str) { | |
const arr = str.split(''); | |
for (let idx = arr.length - 1; idx > 0; idx--) { | |
const rand = getCryptoStrongRandomInteger(idx + 1); | |
const tmp = arr[rand]; | |
arr[rand] = arr[idx]; | |
arr[idx] = tmp; | |
} | |
return arr.join(''); | |
} | |
/** | |
* @param {string} charset | |
* @returns {string} | |
*/ | |
function getRandomCharFrom (charset) { | |
return charset.charAt(getCryptoStrongRandomInteger(charset.length)); | |
} | |
/** | |
* @param {number} max | |
* @returns {number} | |
*/ | |
function getCryptoStrongRandomInteger (max) { | |
// there's a function that retrieves the Web Crypto API | |
// which defers to importing a polyfill of Node's crypto module, | |
// that requires the Web Crypto API. | |
// but we're skipping all of that here. | |
const buf = new Uint16Array(1); | |
crypto.getRandomValues(buf); | |
return buf[0] % max; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment