195 lines
5.7 KiB
PHP
195 lines
5.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Generators;
|
|
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
/**
|
|
* High Entropy Password Generator
|
|
*
|
|
* Generates cryptographically secure random passwords with customizable
|
|
* character sets based on user preferences.
|
|
*/
|
|
class HighEntropyGenerator extends AbstractPasswordGenerator
|
|
{
|
|
private const GENERATOR_ID = 'high_entropy';
|
|
|
|
private const LOWERCASE = 'abcdefghijklmnopqrstuvwxyz';
|
|
private const UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
private const NUMBERS = '0123456789';
|
|
private const SYMBOLS = '!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
|
|
public function __construct(?LoggerInterface $logger = null)
|
|
{
|
|
parent::__construct($logger);
|
|
}
|
|
|
|
public function getId(): string
|
|
{
|
|
return self::GENERATOR_ID;
|
|
}
|
|
|
|
public function getVersion(): string
|
|
{
|
|
return '1.1.0';
|
|
}
|
|
|
|
public function getName(): string
|
|
{
|
|
return 'High Entropy';
|
|
}
|
|
|
|
public function getDescription(): string
|
|
{
|
|
return 'Cryptographically secure random passwords with customizable character sets';
|
|
}
|
|
|
|
/**
|
|
* Centralized option definitions
|
|
*/
|
|
public function getOptionDefinitions(): array
|
|
{
|
|
$options = [
|
|
'length' => [
|
|
'type' => 'integer',
|
|
'default' => 16,
|
|
'min' => 8,
|
|
'max' => 128,
|
|
'required' => false,
|
|
'group' => 'basic',
|
|
'description' => 'Total length of the generated password'
|
|
],
|
|
'include_uppercase' => [
|
|
'type' => 'boolean',
|
|
'default' => true,
|
|
'required' => false,
|
|
'group' => 'character_set',
|
|
'description' => 'Include uppercase letters (A-Z)'
|
|
],
|
|
'include_lowercase' => [
|
|
'type' => 'boolean',
|
|
'default' => true,
|
|
'required' => false,
|
|
'group' => 'character_set',
|
|
'description' => 'Include lowercase letters (a-z)'
|
|
],
|
|
'include_numbers' => [
|
|
'type' => 'boolean',
|
|
'default' => true,
|
|
'required' => false,
|
|
'group' => 'character_set',
|
|
'description' => 'Include numbers (0-9)'
|
|
],
|
|
'include_symbols' => [
|
|
'type' => 'boolean',
|
|
'default' => true,
|
|
'required' => false,
|
|
'group' => 'character_set',
|
|
'description' => 'Include symbols (e.g., !@#$%)'
|
|
],
|
|
];
|
|
|
|
return $options;
|
|
}
|
|
|
|
public function generate(int $count = 1, array $options = []): array
|
|
{
|
|
$options = array_merge($this->getDefaultOptions(), $options);
|
|
$passwords = [];
|
|
|
|
for ($i = 0; $i < $count; $i++) {
|
|
$passwords[] = $this->generateSingle($options);
|
|
}
|
|
|
|
return $passwords;
|
|
}
|
|
|
|
private function generateSingle(array $options): string
|
|
{
|
|
$characterSet = $this->buildCharacterSet($options);
|
|
$length = $options['length'];
|
|
|
|
if (empty($characterSet)) {
|
|
throw new \RuntimeException('No character types selected');
|
|
}
|
|
|
|
// Generate password ensuring at least one character from each selected type
|
|
$password = $this->generateWithGuaranteedTypes($characterSet, $length, $options);
|
|
|
|
// Shuffle the password to randomize position of guaranteed characters
|
|
return $this->shuffleString($password);
|
|
}
|
|
|
|
private function buildCharacterSet(array $options): string
|
|
{
|
|
$characterSet = '';
|
|
|
|
if ($options['include_lowercase'] ?? false) {
|
|
$characterSet .= self::LOWERCASE;
|
|
}
|
|
|
|
if ($options['include_uppercase'] ?? false) {
|
|
$characterSet .= self::UPPERCASE;
|
|
}
|
|
|
|
if ($options['include_numbers'] ?? false) {
|
|
$characterSet .= self::NUMBERS;
|
|
}
|
|
|
|
if ($options['include_symbols'] ?? false) {
|
|
$characterSet .= self::SYMBOLS;
|
|
}
|
|
|
|
return $characterSet;
|
|
}
|
|
|
|
private function generateWithGuaranteedTypes(string $characterSet, int $length, array $options): string
|
|
{
|
|
$password = '';
|
|
$guaranteedChars = [];
|
|
|
|
// Add at least one character from each selected type
|
|
if ($options['include_lowercase'] ?? false) {
|
|
$guaranteedChars[] = self::LOWERCASE[random_int(0, strlen(self::LOWERCASE) - 1)];
|
|
}
|
|
|
|
if ($options['include_uppercase'] ?? false) {
|
|
$guaranteedChars[] = self::UPPERCASE[random_int(0, strlen(self::UPPERCASE) - 1)];
|
|
}
|
|
|
|
if ($options['include_numbers'] ?? false) {
|
|
$guaranteedChars[] = self::NUMBERS[random_int(0, strlen(self::NUMBERS) - 1)];
|
|
}
|
|
|
|
if ($options['include_symbols'] ?? false) {
|
|
$guaranteedChars[] = self::SYMBOLS[random_int(0, strlen(self::SYMBOLS) - 1)];
|
|
}
|
|
|
|
// Add guaranteed characters
|
|
$password = implode('', $guaranteedChars);
|
|
|
|
// Fill remaining length with random characters from full set
|
|
$remainingLength = $length - count($guaranteedChars);
|
|
for ($i = 0; $i < $remainingLength; $i++) {
|
|
$password .= $characterSet[random_int(0, strlen($characterSet) - 1)];
|
|
}
|
|
|
|
return $password;
|
|
}
|
|
|
|
private function shuffleString(string $string): string
|
|
{
|
|
$characters = str_split($string);
|
|
|
|
// Fisher-Yates shuffle using cryptographically secure random
|
|
for ($i = count($characters) - 1; $i > 0; $i--) {
|
|
$j = random_int(0, $i);
|
|
[$characters[$i], $characters[$j]] = [$characters[$j], $characters[$i]];
|
|
}
|
|
|
|
return implode('', $characters);
|
|
}
|
|
}
|