UNPKG

ts-pwgen

Version:

A command line password generator in TypeScript

231 lines (230 loc) 9.04 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PasswordGenerator = void 0; require("colors"); const inquirer = __importStar(require("inquirer")); const copyPaste = __importStar(require("copy-paste")); const crypto = __importStar(require("crypto")); const charsets_1 = require("./charsets"); const defaultOptions = { lowercaseLetters: true, uppercaseLetters: true, numbers: true, specialCharacters: true, latin1Characters: false, parts: { amount: 1, length: 30, delimiter: '-', }, }; class PasswordGenerator { constructor(options = defaultOptions) { this.options = options; } /** * Checks if a password string contains at least one character from a string * array. */ containsFromCharset(password, charset) { for (let char of charset) { if (password.indexOf(char) !== -1) { return true; } } return false; } /** * Count how many charsets are being used in the current configuration. */ countActiveCharsets() { return [ this.options.lowercaseLetters, this.options.uppercaseLetters, this.options.numbers, this.options.specialCharacters, this.options.latin1Characters, ].reduce((prev, curr) => (prev += +curr), 0); } /** * Returns the password length, taking all parts and potential delimiters into * account. */ get passwordLength() { let { amount, length, delimiter } = this.options.parts; return amount * length + (amount - 1) * delimiter.length; } /** * Generate a cryptographically more secure random number than Math.random(). */ random() { let hexRepresentation = '3ff' + crypto.randomBytes(6).toString('hex') + '0'; return Buffer.from(hexRepresentation, 'hex').readDoubleBE(0) - 1; } /** * Generates a password based on this.options. This method will recursively * call itself if the password does not contain at least one character from * each specified charset. */ generate() { let list = []; // This will hold all the characters that are going to be used let password = ''; if (this.passwordLength < this.countActiveCharsets()) { throw new Error('Cannot generate a password with the current configuration'); } if (this.options.lowercaseLetters) { list = list.concat(charsets_1.lowercaseLettersList); } if (this.options.uppercaseLetters) { list = list.concat(charsets_1.uppercaseLettersList); } if (this.options.numbers) { list = list.concat(charsets_1.numbersList); } if (this.options.specialCharacters) { list = list.concat(charsets_1.specialCharactersList); } if (this.options.latin1Characters) { list = list.concat(charsets_1.latin1List); } // If the parts have a length of 0 or below, abort. if (this.options.parts.length <= 0) { return { value: '', charsetLength: list.length, }; } let { amount, length, delimiter } = this.options.parts; for (let partIndex = 0; partIndex < amount; partIndex++) { let part = ''; while (part.length < length) { let randomIndex = Math.floor(this.random() * list.length); part += list[randomIndex]; } // If this is not the last part, add the delimiter. if (partIndex !== amount - 1) { part += delimiter; } password += part; } // Make sure that at least one character from each used charset is present, // otherwise call this method again. if ((this.options.lowercaseLetters && !/[a-z]/.test(password)) || (this.options.uppercaseLetters && !/[A-Z]/.test(password)) || (this.options.numbers && !/[0-9]/.test(password)) || (this.options.specialCharacters && !this.containsFromCharset(password, charsets_1.specialCharactersList)) || (this.options.latin1Characters && !this.containsFromCharset(password, charsets_1.latin1List))) { return this.generate(); } return { value: password, charsetLength: list.length, }; } /** * Generates any positive amount of passwords using this.generate(). */ generateMultiple(amount) { let passwords = []; for (let i = 0; i < amount; i++) { passwords.push(this.generate()); } return passwords; } /** * Interactively ask the user which password they would like if they ask for * more than 1. Also copies it to the clipboard. */ interactive(amount, verbose = false, noClipboard = false) { return __awaiter(this, void 0, void 0, function* () { let passwords; let chosenPassword = ''; try { passwords = this.generateMultiple(amount); } catch (error) { return console.log(error.message); } if (amount <= 0) { return; } if (verbose) this.logInformation(passwords[0].value, passwords[0].charsetLength); if (amount === 1) { console.log(passwords[0].value); chosenPassword = passwords[0].value; } else if (noClipboard) { passwords.forEach((pw) => console.log(pw.value)); } else { let answer = yield inquirer.prompt([ { type: 'list', name: 'password', message: 'Choose password:', choices: passwords.map((pw) => pw.value), }, ]); chosenPassword = answer.password; } if (!noClipboard) { copyPaste.copy(chosenPassword, () => { console.log('\nPassword successfully copied to clipboard!'.gray); process.exit(0); }); } else { process.exit(0); } }); } /** * Log information about the security of the current password options. */ logInformation(password, charsetLength) { const round = (input) => Math.round(input * 100) / 100; const ageOfUniverse = 4.3 * Math.pow(10, 17); const secondsInYear = 31540000; const combinations = Math.pow(charsetLength, password.length); const secs = round(combinations / (2 * Math.pow(10, 12))); console.log(`Password length: `.gray + password.length); console.log(`Different characters: `.gray + charsetLength); console.log(`Possible combinations: `.gray + combinations); console.log(`\nRequired time to crack (10^12 passwords/s)`.gray.underline); console.log(` Seconds: `.gray + secs); console.log(` Years: `.gray + round(secs / secondsInYear)); console.log(` Age of the universe: `.gray + round(secs / ageOfUniverse), '\n'); } } exports.PasswordGenerator = PasswordGenerator;