feat: add Random Generator tool
Cryptographically secure generator for 5 types: - Password: configurable charset + entropy strength meter - UUID: crypto.randomUUID() - API Key: hex/base62/base64url with optional prefix - Hash: SHA-1/256/512 of custom input or random data - Token: variable byte-length in hex or base64url All using Web Crypto API — nothing leaves the browser. Registered in lib/tools.tsx with RandomIcon (dice). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
118
lib/random/generators.ts
Normal file
118
lib/random/generators.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
const CHARSET = {
|
||||
uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
lowercase: 'abcdefghijklmnopqrstuvwxyz',
|
||||
numbers: '0123456789',
|
||||
symbols: '!@#$%^&*()-_=+[]{}|;:,.<>?',
|
||||
hex: '0123456789abcdef',
|
||||
base62: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
|
||||
};
|
||||
|
||||
export interface PasswordOpts {
|
||||
length: number;
|
||||
uppercase: boolean;
|
||||
lowercase: boolean;
|
||||
numbers: boolean;
|
||||
symbols: boolean;
|
||||
}
|
||||
|
||||
export interface ApiKeyOpts {
|
||||
length: number;
|
||||
format: 'hex' | 'base62' | 'base64url';
|
||||
prefix: string;
|
||||
}
|
||||
|
||||
export interface HashOpts {
|
||||
algorithm: 'SHA-1' | 'SHA-256' | 'SHA-512';
|
||||
input: string;
|
||||
}
|
||||
|
||||
export interface TokenOpts {
|
||||
bytes: number;
|
||||
format: 'hex' | 'base64url';
|
||||
}
|
||||
|
||||
function randomBytes(n: number): Uint8Array {
|
||||
const arr = new Uint8Array(n);
|
||||
crypto.getRandomValues(arr);
|
||||
return arr;
|
||||
}
|
||||
|
||||
function toHex(bytes: Uint8Array): string {
|
||||
return Array.from(bytes)
|
||||
.map((b) => b.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
}
|
||||
|
||||
function toBase64url(bytes: Uint8Array): string {
|
||||
return btoa(String.fromCharCode(...bytes))
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=/g, '');
|
||||
}
|
||||
|
||||
export function generatePassword(opts: PasswordOpts): string {
|
||||
let charset = '';
|
||||
if (opts.uppercase) charset += CHARSET.uppercase;
|
||||
if (opts.lowercase) charset += CHARSET.lowercase;
|
||||
if (opts.numbers) charset += CHARSET.numbers;
|
||||
if (opts.symbols) charset += CHARSET.symbols;
|
||||
if (!charset) charset = CHARSET.lowercase + CHARSET.numbers;
|
||||
|
||||
const bytes = randomBytes(opts.length * 4);
|
||||
let result = '';
|
||||
let i = 0;
|
||||
while (result.length < opts.length && i < bytes.length) {
|
||||
const idx = bytes[i] % charset.length;
|
||||
result += charset[idx];
|
||||
i++;
|
||||
}
|
||||
return result.slice(0, opts.length);
|
||||
}
|
||||
|
||||
export function passwordEntropy(opts: PasswordOpts): number {
|
||||
let size = 0;
|
||||
if (opts.uppercase) size += 26;
|
||||
if (opts.lowercase) size += 26;
|
||||
if (opts.numbers) size += 10;
|
||||
if (opts.symbols) size += CHARSET.symbols.length;
|
||||
if (size === 0) size = 36;
|
||||
return Math.round(Math.log2(size) * opts.length);
|
||||
}
|
||||
|
||||
export function generateUUID(): string {
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
|
||||
export function generateApiKey(opts: ApiKeyOpts): string {
|
||||
const bytes = randomBytes(opts.length * 2);
|
||||
let key: string;
|
||||
switch (opts.format) {
|
||||
case 'hex':
|
||||
key = toHex(bytes).slice(0, opts.length);
|
||||
break;
|
||||
case 'base64url':
|
||||
key = toBase64url(bytes).slice(0, opts.length);
|
||||
break;
|
||||
case 'base62': {
|
||||
const cs = CHARSET.base62;
|
||||
key = Array.from(bytes)
|
||||
.map((b) => cs[b % cs.length])
|
||||
.join('')
|
||||
.slice(0, opts.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return opts.prefix ? `${opts.prefix}_${key}` : key;
|
||||
}
|
||||
|
||||
export async function generateHash(opts: HashOpts): Promise<string> {
|
||||
const data = opts.input.trim() || toHex(randomBytes(32));
|
||||
const encoded = new TextEncoder().encode(data);
|
||||
const hashBuffer = await crypto.subtle.digest(opts.algorithm, encoded);
|
||||
return toHex(new Uint8Array(hashBuffer));
|
||||
}
|
||||
|
||||
export function generateToken(opts: TokenOpts): string {
|
||||
const bytes = randomBytes(opts.bytes);
|
||||
return opts.format === 'hex' ? toHex(bytes) : toBase64url(bytes);
|
||||
}
|
||||
Reference in New Issue
Block a user