1
0
mirror of https://github.com/XFox111/PasswordGeneratorExtension.git synced 2026-04-22 08:08:01 +03:00
Files
PasswordGeneratorExtension/utils/PasswordGenerator.ts
T
Eugene Fox 3ecb6c4a31 Major 4.0 (#380)
- Migrated to WXT
- Migrated to NPM
- Added Insert & copy action
- Added ESLint
2024-09-25 16:19:12 +03:00

129 lines
3.6 KiB
TypeScript

import { GeneratorOptions } from "./storage";
const Characters =
{
Uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
Lowercase: "abcdefghijklmnopqrstuvwxyz",
Numeric: "1234567890",
Special: "!#$%&*+-=?@^_"
};
const Similar: string = "iIl1Lo0O";
const Ambiguous: string = "{}[]()/\\'\"`~,;:.<>";
export const CharacterHints = { ...Characters, Similar, Ambiguous };
/**
* Generates a random password
* @param options Options for password generation
* @returns Randomly generated password
* @throws Error if options are invalid
*/
export function GeneratePassword(options: GeneratorOptions): string
{
ValidateOptions(options);
let password: string = GetRequiredCharacters(options);
const availableCharacters: string = GetAvailableCharacters(options);
for (let i = password.length; i < options.Length; i++)
{
const character: string = PickRandomFromArray(availableCharacters);
if (options.ExcludeRepeating && password.includes(character))
i--;
else
password += character;
}
password = ShuffleString(password);
return password;
}
/**
* Validates options for password generation
* @param options Options for password generation
* @throws Error if options are invalid
*/
export function ValidateOptions(options: GeneratorOptions): void
{
if (options.Length < 4)
throw new Error(i18n.t("errors.too_short"));
const availableCharacters: string = GetAvailableCharacters(options);
if (availableCharacters.length < 1)
throw new Error(i18n.t("errors.no_characters"));
if (options.ExcludeRepeating && options.Length > availableCharacters.length)
throw new Error(i18n.t("errors.too_long"));
}
// Returns a string containing all characters that are available for password generation
function GetAvailableCharacters(options: GeneratorOptions): string
{
let availableCharacters: string = "";
for (const [key, value] of Object.entries(Characters))
if (options[key as keyof GeneratorOptions])
availableCharacters += value;
if (options.ExcludeSimilar)
availableCharacters = availableCharacters.replace(new RegExp(`[${Similar}]`, "g"), "");
if (options.Special && !options.ExcludeAmbiguous)
availableCharacters += Ambiguous;
return availableCharacters;
}
// Returns a string containing all characters from every available set that are required for password generation
function GetRequiredCharacters(options: GeneratorOptions): string
{
let result: string = "";
const characters: Record<string, string> = Object.assign({}, Characters);
if (!options.ExcludeAmbiguous)
characters.Special += Ambiguous;
if (options.ExcludeSimilar)
for (const key of Object.keys(characters))
characters[key] = characters[key].replace(new RegExp(`[${Similar}]`, "g"), "");
for (const [key, value] of Object.entries(characters))
if (options[key as keyof GeneratorOptions])
result += PickRandomFromArray(value);
return result;
}
// Picks a random character from a string
function PickRandomFromArray(array: string): string
{
return array[GetRandomInt(0, array.length)];
}
// See https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Math/random
// min is inclusive, max is exclusive
function GetRandomInt(min: number, max: number): number
{
const arr = new Uint16Array(1);
crypto.getRandomValues(arr); // Using crypto instead of Math.random() as a CSPRNG
return Math.floor((arr[0] / 65_536) * (max - min)) + min;
}
// Shuffles a string using Fisher-Yates algorithm and CSPRNG
// See https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
function ShuffleString(str: string): string
{
const arr = str.split("");
for (let i = arr.length - 1; i > 0; i--)
{
const j = GetRandomInt(0, i + 1);
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr.join("");
}